My debouncing/button routines. Comments needed :)

Go To Last Post
50 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At a quick glance, I don't think too much of it.

-- Every 1ms? Have you looked at typical bounce times in Ganssle? 10ms is fine for most work; sometimes 5ms might work better.
-- You declare your button value "unstable" as soon as there is a different value. In all of my work, the button retains its stable value until another stable value is verified.
-- Time your every-1ms routine. Then consider if you have say 16 inputs/buttons to debounce. I'm guessing with all the indexing and such you are using up most of your AVR just doing the simple task.

Both danni and I have posted parallel debounce routines that take a few microseconds every sample. Search "debounce danni lee" and/or "debouncing danni lee" for extensive prior discussions.

In nearly all of my apps I compute (and use) the rising edge values as well as the current state values. In a number of apps I also compute (and use) the falling edge values.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's really a very huge amount of code.
I assume, you use always only the biggest AVR (ATmega2560).

This task can be solved easily with an ATtiny24.
But I fear, not with your huge code.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I concur. The problem is easily solved in 5 lines of C code for generating a 5 or 10 ms "tick" from a timer, and less than 10 lines of C code for the debouncer that runs on every such "tick" (i.e. the timer ISR).

I am fond of Dannis implementation, that sports the footprint I describe. Reading the stable state of a button is a "one-liner". The nicest thing is that it does up to 8 buttons this way for no extra cost at all. If you need 16 buttons instead, you will need to add a few (<5?) lines of C code, and about 5 static variables will be 16-bit instead of 8-bit.

Danni has also posted a variant once that sports auto-repeat (with configurable times for the delay before the auto-repeat starts, and for the repeat-frequency). IIRC he had to add a few lines of C code for that, and probably needed a few more variables..

(I think Dannis debouncer is a small piece of embedded art, if that does not come through clearly from the above...)

Please don't get me wrong. I am not the one striving for the most compact code. Regulars here will assert that I often argue that optimizing for maintenance is often a good thing - meaning you write readable/maintainable code, possibly at the cost of executable code size.

Still, debouncing is a "static problem" as long as the hardware is fairly stable (i.e. you are using AVRs that all share the same way to do digital IO) then such a piece of code should be very stable. There is not much that can change in the environment so that it will need to be re-written in any substantial way. Also, debouncing is a highly repetitive task. Thus it makes sense not to make it "non-compact" (I'm obviously trying to be vague here..).

And yes, 1 ms is a very high sampling frequency for the problem at hand.

4 identical snapshots, spaced 5 to 10 ms, is reasonable for most applications. If the button is marked "Detonate nuclear device (one for each press of the button)" I'd be more conservative.. :D

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok thanks for the comments. It seems I have to do much more studying on this (among many other things).

I red your debouncing code (C_TAST.C) a few times Peter but I didn`t quite get it yet but maybe I`ll do if I`ll just go over it enough times. Just seeing many bit operations done on variables dont just open it up so easy. Do you got any words for me so I`d understand it better :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Looks like something I would write if I were making a general purpose debouncing library for everyone to use - it does its job and is very generic.

But being generic has the downside of being too complex for a specific debouncing job where there might be few buttons on the pins of same IO port.

Danni's code is ingenious, I once analyzed how it works, but can't remember anymore.

I usually like the shift register style debouncing, where button bits are shifted in into a variable, and if variable is full of 1s then button state is set to reliable 1 and if variable is full of 0s then button state is set to reliable 0, all other values mean the button is unstable and button state is unchanged. Good for independent buttons.

However if it is needed to debounce buttons that depend on each other, say two buttons, button A initiates function A, button B initiates function B, but "simultaneous" push of buttons A and B must initiate function C, again a different approach is needed. There you need to wait until all buttons have been stable for enough time.

I have not yet tried this, but interrupt pins could be the best way of debouncing buttons in hardware. Not by actually generating an interrupt, but just for reading the flag bit. If pins have been configured for interrupt on change, any change will set the interrupt flag bit. Then in a timer interrupt, all you need to do is to read the interrupt flag bit if there has been a change or not. If there was a change since last read, this reading is unstable and can be discarded, and the interrupt flag is reset. If there was no change since last read, the current reading is stable. You just have to count how long the reading needs to be stable before believing the button has really changed state, and the hardware notifies any unstability with the interrupt flag.

But lately all my pushbuttons have been connected through I2C IO expander so there is little need to debounce, either I get up or down state and the button polling rate is so slow through I2C that bounce is never seen, and I can just react to the push edge.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At the centre of Peters algorithm is the concept of vertical counters. I wrote a short explanation in another thread. Others followed it up, and threw in other parts of the explanation.

As I say there, when I first saw the code I was also confused. But paper, pen and an hour or two on a boring conference solved that. :D

And I have to correct myself on one claim in an earlier post. I said that reading out the steady state was a one-liner. That is wrong. I'ts a five.liner. (If you just want to look for a possible make or break, then it's a one-liner. If you want to 'consume' it too - i.e. throwing the transition away after you've seen it - then it's five lines (incl cli() and sei() for atomic access, so that the readout and the ISR do not fight over the key_state variable.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Others followed it up, and threw in other parts of the explanation.

Quote:

As I say there, when I first saw the code I was also confused.

In other words, when you first followed the explanation you threw up.
Quote:

the concept of vertical counters.

I note that neither you nor OP has embraced my "horizontal counters". :twisted:

danni has extensions that I tend to provide externally such as held-down periods--in nearly all my apps I find that only a few buttons will require that. I'll also note that I use the debounce mechanism for other inputs, e.g. from a PLC or flowmeter, that may have noise spikes and ringing and the like analogous to buttons/switches/keys.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I note that neither you nor OP has embraced my "horizontal counters". Twisted Evil

No, Lee. But for me it suffices with one already existing implementation. If I understand it, if it is compact, and if it has the lovely feature of being expandable (i.e. more buttons for no or a very small cost) then I'm stisfied. The OP evidently looks at this in another way, or haven't been able to find something that worked/made sense/whatever.

Had I stumbled upon your solution first it might have been that one I had praised. You're just a victim of the residual term at the end of the formula (a long time ago, I got one of my distinguished grades in 'Probability and theory of inference". Didn't do quite as well in "Applied Statistics" though. I blame the darned Epsilon for that..). :D

But this thread could be well served by a link to that thread where you presented it! (You'll fix that much faster than I would - we all know you carry a piece of paper in your wallet with the URL at all times.. :wink:)

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey, Lee! I was really serious when I said that a link to the "button debounce shootout thread" would make a great contribution to this thread. And jokes/teasing aside, I think you will find it much faster than I could..

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Hey, Lee! I was really serious when I said that a link to the "button debounce shootout thread" would make a great contribution to this thread. And jokes/teasing aside, I think you will find it much faster than I could..

I've looked for it several times, and again today. Even using Google site search I can't come up with the "definitive" thread where danni and I went back-and-forth cycle-counting. Perhaps it got lost in a Forum reorg; some links are definitely broken. The discussion started when in 2006 Spamiam disputed my claim that proper debouncing only takes a few microseconds every few milliseconds, or about 0.1% of an AVR's bandwidth.
http://www.avrfreaks.net/index.p...
...and an extensive follow-up discussion a month later that is interesting reading:
http://www.avrfreaks.net/index.p...

My routines have evolved over time. A sequence was handed down to me by a mentor which was then implemented in AVR. For 8 bits in parallel it takes about a dozen cycles with intermediate steps held in registers, and leaves current state (PCS) and rising edges (PCE) in registers for use:

IOSCAN:   ; GET AND DEBOUNCE STATE OF PORT C SWITCHES (6)
   MOV   TMPB,PCX2   ; GET 3x PENDING CHANGES
   MOV   PCX2,PCX1   ; GET 2x PENDING CHANGES
   IN   PCX1,PINC   ; GET ACTUAL PORT C INPUTS
   LDI   TMPC, PCMASK   ; LOAD THE INVERT MASK--ACTIVE LOW
   EOR   PCX1,TMPC   ; INVERT SELECTED INPUTS
   EOR   PCX1,PCS   ; GET CHANGES ONLY
   AND   TMPB,PCX1   ; AND 3X WITH 1X
   AND   TMPB,PCX2   ; AND RESULT WITH 2X
   EOR   PCS,TMPB   ; MAKE 3X CHANGES
   EOR   PCX1,TMPB   ; REMOVE 3X CHANGES FROM PENDING
   EOR   PCX2,TMPB   ; REMOVE 3X CHANGES
   AND   TMPB,PCS   ; GET ON STATE CHANGES ONLY
   OR   PCE,TMPB   ; SET ACTIVE EDGE TRIGGERS 

Now I'll post a couple C versions. No, they won't be quite as tight but don't burn as many registers. Let's say they take 10 microseconds every 10 milliseconds. Maybe a bit more for the 16-bit wide, table-driven version. Still less than 1% of AVR bandwidth even including ISR overhead, function call, fetching the signals from the port.

Translating the ASM version to a C version with a fixed number of intermediate states ends up with something like below. Posted is a 16-bit-wide version; just change the data types to "unsigned char" for 8 bits.

unsigned int				key_debounce		(unsigned int switches)
{
unsigned int	work;
unsigned int	prev;	// calculate the falling edges

	prev = key_current;			// save for calculating falling edges

	work = key_bounce3;			// move prior states closer to validated
	key_bounce3 = key_bounce2;
	key_bounce2 = key_bounce1;
	key_bounce1 = switches;		// get the new strobe data

	key_bounce1 ^= key_current;	// only concerned with changes from current
	work &= key_bounce1;		// make sure there are no bounces
	work &= key_bounce2;
	work &= key_bounce3;
	key_current ^= work;		// make validated changes to current
	key_bounce1 ^= work;		//  and remove from pending stages
	key_bounce2 ^= work;
	key_bounce3 ^= work;

	key_edge = work & key_current;		// only want "on" transitions
									// Note that edges are NOT saved from one
									// call of this routine to the next --
									// "Use it or lose it."  [Change "=" to "|="
									// to save the edges until explicitly cleared.]
    service_needed_key = (key_edge != 0);	// set the new keypress flag

// Calculate the falling edges.  These will be bits that changed to 0 from the "prev"
//	pass of affirmed 1 bits in key_current, to the current pass.
	key_falling = (key_current ^ prev) & prev;

	return (key_edge);			// new debounced edges are 1 bits
}

Validated current state is in key_current; rising edges in key_edge; falling edges in key_falling, and a separate global flag for any new edges. (In later generations I generally make key_edge a global register variable so there is no need for the separate flag as the test of key-edge is very simple.

As time went on and there are more and more apps, the above evolved into a table-driven version. Fragments have been posted over the years. With this version the number of intermediate states can be determined at compile time, and in addition one debounce routine can be used to process e.g. inputs in one table and pushbuttons in another.

// **************************************************************************
// *
// *		S I G N A L _ D E B O U N C E
// *
// **************************************************************************
//
//	Input a set of up to 8 bit values as "value".
//	Enter the parameter value into a multiple-debounce sequence based on
//	the define NUM_IN_A_ROW, which represents the number of consecutive
//	signal states to signify a new "1" or a new "0".
//
//	Based on the sequence used for debouncing switch inputs.
//
//	1)	Save penultimate value to "work"
//	2)	Promote each value less than penultimate to next higher index
//	3)	First entry receives new "value"
//	4)	Get changes from current, debounced state:
//			first entry ^= ultimate entry
//	5)	Propogate any bounces:  for each entry from first to penultimate,
//			work &= entry
//	6)	Propogate valid changes:  for all entries,
//			entry ^= work
//
//	The ultimate entry is returned as the value.
//
unsigned char				signal_debounce		(unsigned char value,
												unsigned char *table)
{
unsigned char	work;
unsigned char	loop;	// loop counter

// 1)
	work = *(table + (NUM_IN_A_ROW - 1));	// move prior states closer to validated
// 2)
	for (loop = NUM_IN_A_ROW - 1; loop > 0; loop--)
		{
		*(table + loop) = *(table + loop - 1);
		}
// 3)
	*table = value;							// get the new strobe data
// 4)
	*table ^= *(table + NUM_IN_A_ROW);		// only concerned with changes from current
// 5)
											// make sure there are no bounces
	for (loop = 0; loop < NUM_IN_A_ROW; loop++)
		{
		work &= *(table + loop);
		}
// 6)
											// make validated changes to current
	*(table + NUM_IN_A_ROW) ^= work;
											//  and remove from pending stages
	for (loop = 0; loop < NUM_IN_A_ROW; loop++)
		{
		*(table + loop) ^= work;
		}

	return (*(table + NUM_IN_A_ROW));		// return the multi-debounced value
}

Example of usage:

// Keypad debouncing
#define	NUM_IN_A_ROW	4					// number of consecutive values to "trip"
unsigned char	inputs[NUM_IN_A_ROW+1];		// input signals


register unsigned char	input_current;				// returned from signal_debounce
unsigned char	input_prev;					// for calculating rising & falling edges
register unsigned char	input_rising;
unsigned char	input_falling;
...
// ***********
// Switches -- every 10 ms.
// ***********
		// Fetch switch state every 10ms.
		input_prev = input_current;	// for calculating edges

		scratch = key_fetch();		// strobe and process switch inputs
		input_current = signal_debounce(scratch, inputs);		//  and create edge triggers & current state

// Rising & falling edges
//
// Note:  no edges.  If needed:
//	Rising & falling edges = composite_io ^ previous_io;
//	Rising edges = (composite_io ^ previous_io) & composite_io;
//	Falling edges = (composite_io ^ previous_io) & previous_io;
//
//		rising_io = (composite_io ^ previous_io) & composite_io;
//		falling_io = (composite_io ^ previous_io) & previous_io;

		input_rising = (input_current ^ input_prev) & input_current;
		input_falling = (input_current ^ input_prev) & input_prev;

Yes, I could do edges in the routine itself but I don't care about a few cycles. The above is "good enough" in most cases.

I've done a couple apps with 32 bits wide.

Now we should all be able to find my code. ;) Sorry for the derailment, JauVi.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can try this example on the STK500:

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

The debounce interval was set to 1s.
So you can see, how the states change after pressing or releasing the key.
And you see also that a change was only accepted, if it was longer that 4s.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jepael wrote:
Looks like something I would write if I were making a general purpose debouncing library for everyone to use

Thats exactly what I was trying to do. I was trying to write this general debouncing/button handling module that I could very easily add to any project. Clearly I lack the skill to do such. But no matter, I`ll just concentrate on understanding Peter`s solution.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Don't get me wrong - what you have written is a good start and may be very useful for someone that hears about debounce the first time. In that case Peter's solution may be too scary because it is not so simple to understand.

And didn't your matrix scanning routines have some kind of debounce as well?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I`m not offended in any way. Yeah it did but it was the first attempt to anyhow eliminate bounce for me and it was something like this:

if (input_low)
  {
  delay
  do the thing
  while (input_low) {}
  delay
  }

Anyway I`ll study and mayde I´ll come up with something usefull for programmers even more amateur than me :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I`ve been going trough the code now for a couple of times and I keep
getting it like this.

I`ll use just one bit let`s say bit0 for the sake of simplicity.
In this example the button in bit0 is just pressed.

Starting position:


KEY_INPUT bit0 = 0
key_state bit0 = 0
key_press bit0 = 0
i bit0 = 0
ct0 bit0 = 0
ct1 bit0 = 0

Okay so the ISR starts.

i = key_state ^ ~KEY_INPUT;

         0 // key_state
     XOR 1 // ~KEY_INPUT
         1 = i

Next

ct0 = ~( ct0 & i );

         0 // ct0
     AND 1 // i
     NOT 0
         1 = ct0

ct1 = ct0 ^ (ct1 & i);

         0 // ct1
     AND 1 // i
         0
     XOR 1 // ct0
         1 = ct1

i &= ct0 & ct1;

         1 // i
         1 // ct0
     AND 1 // ct1
         1 = i

key_state ^= i;

         0 // key_state
     XOR 1 // i
         1 = key_state

key_press |= key_state & i;

         0 // key_press
      OR 1 // key_state
         1
     AND 1 // i
         1 = key_press

Why does the key_press turns to 1 in the first cycle instead of the 4th?
Where is the (possibly) little mistake I`ve made or a bigger one.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

Here is my input library if you want to take a look at it and see if I did anything differently that might be helpful:

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

Good luck,

Alan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JauVi wrote:

Why does the key_press turns to 1 in the first cycle instead of the 4th?
Where is the (possibly) little mistake I`ve made or a bigger one.

The default state of the ct0-, ct1-bits was 1,1.

The reason was, that after 4 counts it was 1,1 again.
And only then i & ct0 & ct1 gives 1.
If I would use 0,0 as default, I need further negations to get 1:
i & ~ct0 & ~ct1

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I always use the following code to debounce my buttons:

bool KeyPressed( char key )
{
  if ( bit_is_set( PORTD, key ) )   // if key is pressed
  {
    _delay_ms( 35 );   // debounce delay
    if ( bit_is_set( PORTD, key ) )     // if key is still pressed
    {
      return true;    // then we assume key is pressed
    }
  }

  return false;   // key was not pressed
}

I'm not saying it's the best, but it works.

Note: the 35ms is not some random number. I got it from a button's datasheet. It's the bounce time for that specific key type.

Building my dreams!

Last Edited: Mon. Aug 1, 2011 - 02:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As usual, there are many good solutions, where the measure "good" is very much dependent on the whole project. For some applications the simple stop-and-wait-for-stable solution might be perfectly fine - so use that. For other applications the penalty of loosing e.g. 35 ms execution time to a idle loop might be disastrous. It depends..

The crux is to know when one or the other should be used and to have both "in your tool box".

Aside: I know our own Uncle Bob has remarked this several times, but I repeat it in my wording - there are situations where you do not need to debounce, the obvious example being when you have one button for "on" and another for "off". Even if the on-button bounces a hundred times during 100 ms it does not matter. The thing goes into on-state at the first transition and then you can have 99 following on-events which turns the thing on that is already on.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Johan has a good point, but even in that case, there is an exception to that.

In such situation it may still be necessary to somehow filter glitches coming from ESD spikes or nearby cellphone transmission etc, to believe the button is actually pushed, instead of just a hairy cat walking by.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I had not looked at Peter's de-bouncing code before. However, it does look impressive. The ISR is short & sweet. Initially, it seems a waste of T1, but you can use the interrupt to set a "tick" as well as doing the de-bounce.
Two jobs with one timer. Nice!

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

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A walkthrough of Dannis debouncer

I will present here (without consulting Danni himself :shock:) a walkthrough of "Dannis Debouncer". Since it is a lot of typing work to get it formatted to suit an AVRfreaks post I only go though a few steps with a switch initially open then bouncing and then going into steady closed. For the sincerely curious and ambitious, you can use what I start with and proceed from there to study the inner workings of the debouncer, the "vertical counter" concept and implementation etc..

I hope this helps, and pray I got everything right!

VARIABLES

   // The current debounced steady state
   // Or, if you like, "which keys ARE pressed right now?"
   // Never write to this variable in your application!
   // This is a state variable for the debouncer.
   // You may read it, though.
   char key_state;

   // The currently pending (i.e. not consumed by your aplication)
   // keys that has transitioned into the pressed steady state.
   // Or, if you like, "Which keys HAVE BEEN pressed?
   // Use this to "detect and consume" key presses, i.e.
   // your main app may both read and write this variable.
   char key_press;  

   // The current state of the I/O pins connected to the switches
   char KEY_INPUT;   

   // Eight two-bit vertical counters, each having it's MSB in
   // ct1, and its LSB in ct0
   char ct1, ct0;

   // A scratchpad variable
   char i;

ALGORITHM / CODE
This is the code in the ISR that actually does the debouncing.
The comments are mine, not Peters:

void ISR(void) 
{ 
  // Determine which pins that at this moment have a state differing 
  // from the debounced steady state.
  // If your switches are "active low" then deal with this right here
  // at the input, by replacing KEY_INPUT with ~KEY_INPUT .
  i = key_state ^ KEY_INPUT;

  // Increment the vertical counter for each pin that differs from the steady state,
  // reset all other vertical counters.
  // These two lines actually implement this increment operation with handling of
  // carry from ct0 to ct1. Read the walkthrough to see this in action!
  ct0 = ~( ct0 & i );
  ct1 = ct0 ^ (ct1 & i);

  // Detect which counters have rolled over - these correspond to the pins that have 
  // seen 4 consecutive
 identical states that are the opposite of the steady state
  i &= ct0 & ct1; 

  // Toggle the steady state of those pins
  key_state ^= i;

  // Mirror the new keys pressed into the variable where the main app can 
  // eventually consume them.
  key_press |= key_state & i;
}

WALKTHROUGH
What follows is a walkthrough of a scenario. For the purpose of clarity I will deal with only one switch and one vertical counter. The values of variables are the ones after the statement has been executed, values are noted only for the variable actually assigned in the statement at that step in the walkthrough, so as not to clobber it. (I.e. in order to find the current value of a variable just before the statement start at that row and go up to the latest assigned value.)

The walkthrough starts with the switch open, then closing, then bouncing off one time and then going to steady on. I won't walk longer than that - you can continue the walkthrough yourself. (Do it - it is really a good illustration of the workings of Peters algorithm!

<--for this to be readable the browser must be wide enough for this to show on one line with the arrows-->

Round                                       Statement	                  key_state ct1 ct0   i key_press
=========================================   ============================= ========= === === === =========
Initial values of all variables                                                   0   0   0   0         0
-----------------------------------------
Round 1: Switch open     (KEY_INPUT == 0)   
                                            i = key_state ^ KEY_INPUT;                        0

                                            ct0 = ~( ct0 & i );                           1
                                            ct1 = ct0 ^ (ct1 & i);                    1

                                            i &= ct0 & ct1;                                   0

                                            key_state ^= i;                       0

                                            key_press |= key_state & i;                                 0
-----------------------------------------
Round 2: Switch closed   (KEY_INPUT == 1)   
                                            i = key_state ^ KEY_INPUT;                        1

                                            ct0 = ~( ct0 & i );                           0
                                            ct1 = ct0 ^ (ct1 & i);                    1

                                            i &= ct0 & ct1;                                   0

                                            key_state ^= i;                       0

                                            key_press |= key_state & i;                                 0


-----------------------------------------
Round 3: Switch "bounces open" (KEY_INPUT == 0)
                                            i = key_state ^ KEY_INPUT;                        0

                                            ct0 = ~( ct0 & i );                           1
                                            ct1 = ct0 ^ (ct1 & i);                    1

                                            i &= ct0 & ct1;                                   0

                                            key_state ^= i;                       0

                                            key_press |= key_state & i;                                 0

-----------------------------------------
Round 4: Switch bounces back to closed (KEY_INPUT == 1)  
                                            i = key_state ^ KEY_INPUT;                        1

                                            ct0 = ~( ct0 & i );                           0
                                            ct1 = ct0 ^ (ct1 & i);                    1

                                            i &= ct0 & ct1;                                   0

                                            key_state ^= i;                       0

                                            key_press |= key_state & i;                                 0

-----------------------------------------
Round 5: Switch stays closed (KEY_INPUT == 1)
                                            i = key_state ^ KEY_INPUT;                        1

                                            ct0 = ~( ct0 & i );                           1
                                            ct1 = ct0 ^ (ct1 & i);                    0

                                            i &= ct0 & ct1;                                   0

                                            key_state ^= i;                       0

                                            key_press |= key_state & i;                                 0

-----------------------------------------
Round 6: Switch still closed (KEY_INPUT == 1)
                                            i = key_state ^ KEY_INPUT;                        1

                                            ct0 = ~( ct0 & i );                           0
                                            ct1 = ct0 ^ (ct1 & i);                    0

                                            i &= ct0 & ct1;                                   0

                                            key_state ^= i;                       0

                                            key_press |= key_state & i;                                 0

-----------------------------------------
Round 7:  Switch still closed (KEY_INPUT == 1)
                                            i = key_state ^ KEY_INPUT;                        1

                                            ct0 = ~( ct0 & i );                           1
                                            ct1 = ct0 ^ (ct1 & i);                    1

     A NEW STEADY STATE! >>>                i &= ct0 & ct1;                                   1

                                            key_state ^= i;                       1

                                            key_press |= key_state & i;                                 1

Final comment: Now that you've gone through that at least once you might be confused by the way the counter counts. Yes, it looks a little weird, but what it actually does is to start at 11 and then count down 10, 01, 00 to finally wrap back to 11 at which stage we have seen four consecutive samples of the input pin with identical state (as in round 7 above). Whenever we see a pin state identical to the steady state the counter is reset to 11 (as in round 3).

That's all (for now), folks!

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lee!

I've gently browsed your code for the first time. I am getting a funny vision of a variant of Tetris :D:

- The board is as wide as the number of switches
- The board is a high as the number of required identical samples to deem a steady state
- For each tick everything in the board falls down one step
- The things falling in at the top are the button samples
- Whenever a zero/blank/open_switch falls in at the top, the column beneath it is cleared
- What falls out at the bottom are the newly detected steady states

In my eyes it is just as vertical as Dannis, but while danni keeps a counter of the number of consecutive samples != the steady state that is still needed for a state change, you keep the samples per se in a (horizontal) pipe/column.

Am I close?

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Am I close?


Nice analogy. I figured it out 10 years ago with pencil and paper as you did with Peter's code. Once I had proven it to myself I haven't worried about it anymore. :)

Anyway, if you configure mine with two intermediate states, I think you end up with nearly identical storage and time as Peter's. I'd have to work through all the combinations, but isn't it a lot easier to change the number of intermediate states with my approach? (and even if it is, does that make any difference in practice?)

In the C versions, I think both approaches can be changed 8 <> 16 <> 32 bits just by changing the type of the storage variables.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Anyway, if you configure mine with two intermediate states, I think you end up with nearly identical storage and time as Peter's.

Oh, then my "mental image" of your algorithm is not correct. I actually thought you let each sample "slide through the arrays".

If I rework your code, using array notation rather than the pointer arithmetic, I get something like this (sketchy/edited but not tested):

// 1) 
   work = table[NUM_IN_A_ROW - 1];   // move prior states closer to validated 
// 2) 
   for (i = NUM_IN_A_ROW - 1; i > 0; i--) 
   { 
      table[i]= table[i - 1]; 
   } 
// 3) 
   table[0] = value;                     // get the new strobe data 
// 4) 
   table[0] ^= table[NUM_IN_A_ROW];      // only concerned with changes from current 
// 5) 
                                 // make sure there are no bounces 
   for (i = 0; i < NUM_IN_A_ROW; i++) 
   { 
      work &= table[i]; 
   } 
// 6) 
                                 // make validated changes to current 
   table[NUM_IN_A_ROW] ^= work; 
                                 //  and remove from pending stages 
   for (i = 0; i < NUM_IN_A_ROW; i++) 
   { 
      table[i] ^= work; 
   } 

   return table[NUM_IN_A_ROW];      // return the multi-debounced value 

So... Assuming we are dealing with 8 switches, for an implementation needing four identical samples for a new steady state, you need four 8-bit byte entries in the array (plus one for the steady state, but we wont count that since Peters variant also has to store this, only difference is that he is doing it in a separate variable). For the same four-identical-samples requirement Peter needs two bytes.

To go to eight identical samples required, you'd have to have eight entries (i.e. you need four more bytes) in your array whereas Peter needs to add one extra byte variable. Or, if you like, Peters two (three) bits can count to four (eight) samples. Your four (eight) bits can store four (eight) samples.

But then, as you point out the code needs to be rewritten/extended in his case while your code can be left identical to what it is now.

Do I get it correctly?

(No, I am not in any way stating that one is better than the other. Any sane person can see that both variants have their advantages.)

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

you need four 8-bit byte entries in the array

Quote:

For the same four-identical-samples requirement Peter needs two bytes.

I don't think that is correct, but it may indeed be one more. I'd have to work through it again. Short speculation: I think Peter only need three in a row so there are two intermediate states so it is the same. (Peter can't do magic. Since there are 8 inputs per byte and two counters there are only two bits allocated per counter and thus four combinations. But indeed there may be a bit of magic in there, using the rollover from 11 to 00 to indicate start of a new sequence.)

However, even if there is one extra byte you cannot use the Tetris analogy with danni-code. :lol:

Lee
Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jeez :) It always amazes me how everyone here are so willing to help and with such extent :).
I got how the algorithm works now and like you said Johan, its pretty straightforward once you get it. Though I did`t notice any line in the code which initializes ct0 and ct1 as ones. However this thread surely serves anyone needs to understand the same thing.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Though I did`t notice any line in the code which initializes ct0 and ct1 as ones.

It happens on the very first run of the ISR (before that ct1 and ct0 are both zero, since they are statically allocated variables) - see Round 1. And then it happens as son as we get a sample that is the same as the steady state - see round 3.

Quote:
It always amazes me how everyone here are so willing to help and with such extent

Personally, I'd say that I dizz at least as much as I help. It all depends on the question/problem. While the clueless posts with code typed in (and so bad it is obvious that it's not the actual code), with no search done, no reading research etc gets my "Quality of answers are correlated with quality of question", this thread has really been fun, has challenged at least me with something that actually needs a lot of chewing, investigation and thinking. I'm not at all thrilled by the former type of questions, but am delighted over the latter type.

And then there is the element of feeling good because you help someone. Also knowing that it will be appreciated. From people like you I know that I can expect help with a problem of mine if (uh, when) it arises. Re the clueless I think nothing about if they even will start to actually read and understand the answers they get - I often think its a simple "feed me" attitude. This thread started out so that it was clear that people actually where interested in how different debouncers work. I knew Dannis (but har a very good repetition). Thanks to this thread I now know and understand Lee's. I hope others have gotten something out of it too.

Then there's that good showing-off feeling.. :wink:

The help the clueless actually get from me (at least what help I intend for them to get from me) is to understand why their question will produce little real attention, and how they should act to remedy that.

Quote:
I think Peter only need three in a row so there are two intermediate states so it is the same.

I think not. He counts to four identical samples that differs from the steady state. Yes, he has the value (binary) 11 two times in that sequence. He discerns between these two by since the first 11 is when the sample is the same as the steady state, while at the ending 11 the sample is the opposite of the steady state:

i = key_state ^ KEY_INPUT; 
// i is now set if the sample differs from the steady state
.
.
i &= ct0 & ct1;
// i is now set if we've seen four identical samples that differ from the steady state (N.b. the &=)

So, yes he needs another byte, but this is a local scratchpad variable i in the ISR, and those does not count. :wink:

Again, this is not any strong argument for one or the other solution. Still, having this discussion really helps reason about how they work.

If I should really pick one top argument for each, then it would probably be:

For Peters - against yours: One run of Peters debouncer is executing six not very convoluted C assignment statements. One run of your code will execute 1 + 4 + 1 + 1 + 4 + 1 + 4 + 1 = 17 statements (the "1"s being statements in between the loops, and the "4"s being statements inside the loops. Yes, I know that this is by no means an exhaustive examination of generated code for the both, but I'd be surprised if one run of both would end up with yours being faster. I'd rather expect/speculate (again, really a speculation) that yours will run perhaps three times slower. Not a big deal.

For yours - against Peters: It is very indicative that while I've had to work through the details of Peters algorithm three times over the past three or so years (one forgets the details even after a few days or weeks) yours is really straight forward (especially when I exchanged the pointer arithmetic for array notation :wink:).

And since I often advocate "optimize for maintenance", which often means "write clear and straight forward code, and sacrifice some CPU cycles or memory cells if you can get away with it (i.e. if you're not 100% constipated in time and/or space)" the your variant actually wins if I reason with my self.

Against this stands the nerdy technical fascination by the convoluted compactness of Peters code, and the self-indulgence of that I managed to decode and understand it. Not very good rational arguments..

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Lee
Lee

Can't even debounce his own signature... :roll:

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Can't even debounce his own signature...
Deliberate irony?

--greg
Still learning, don't shout at me, educate me.
Starting the fire is easy; the hardest part is learning how to keep the flame!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Perhaps - Lee is one deviously cunning cow.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, All!

Danni's code is finest!

But reseting counter in middle state make buttons sharply. And if polling made every 10ms, then it may cause delay for 40-80 ms. That is very bad for me (you need press button more than 150 ms; I press button mostly about 70 ms).

I prefer do filter it every 1 ms. Then buttons become smoothy ;-)

Ilya

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, well that is the downside of debouncing. You need to adjust the polling rate and how many stable readings you need until the button is detected. And it all depends on the button, and how it will behave after it has been pushed few thousand times and aged for several years..

Different people also experience the delay differently. And also depends on the application. Sometimes 100ms response time is OK, sometimes 20ms is needed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jepael wrote:
You need to adjust the polling rate and how many stable readings you need until the button is detected.

With filtering you consider how much one state prevails over another. Not count stable readings. It is the conerstone.

And it will work in very noisy environment.

And repeat, Danni's code is perfect!

Ilya

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Friends!

As a final piece of the puzzle re Dannis code, I give you this simulator which should be buildable and runnable on any PC system (my platform was Windoze and MS Visual C++). It's a Genuine 15 Minute Hack[tm] but it does what it should do. :D

Enjoy:

// DanniDebouceSimulator.c

#include 
#include 
#include 

typedef unsigned char uint8_t;

//-- A simple symbol table for durposes of dump output layout -----------------

typedef struct
{
    char name[20];
    int pos;
} SymtabEntry;

const int SYMTAB_SIZE = 10;
SymtabEntry symtab[SYMTAB_SIZE];

int lookup(char * name)
{
    int i = 0;
    while (i < SYMTAB_SIZE && strcmp(symtab[i].name, "") && strcmp(symtab[i].name, name) )
    {
        i++;
    }

    if (i > SYMTAB_SIZE - 1)
    {
        printf("INTERNAL ERROR: Symtab busted!");
        exit(0);
    }

    if ( strlen(symtab[i].name) == 0 )
    {
        // New entry
        strcpy(symtab[i].name, name);
    }

    return i;
}

//-- Functions to dump the state of the debouncer to the screen ---------------

const int COLUMN_WIDTH = 12;

void dumpState(char * statement, char * variable, uint8_t value)
{
    int i;
    i = lookup(variable);

    // We only want to see the lowest bit (as "0" or "1")
    uint8_t lsb = value & 1;
    printf("   %-30s %*X\n", statement, (i+1)*COLUMN_WIDTH, lsb);

}

void dumpLegend()
{
    printf("\n   %-30s ", "STATEMENT");
    int i = 0;
    while (i < SYMTAB_SIZE && strlen(symtab[i].name) != 0 )
    {
        printf("%*s", COLUMN_WIDTH, symtab[i].name );
        i++;
    }
    printf("\n");
}

//-- The debouncer proper -----------------------------------------------------
void debounce(uint8_t keyInput)
{
    static uint8_t keyState;
    static uint8_t ct1, ct0;
    static uint8_t i;
    static uint8_t keyPress;

    static int round;

    printf("\nROUND %i with keyInput == %i\n", round++, keyInput & 1);
    // Determine which pins that at this moment have a state differing 
    // from the debounced steady state. 
    // If your switches are "active low" then deal with this right here 
    // at the input, by replacing KEY_INPUT with ~KEY_INPUT . 
    i = keyState ^ keyInput;
    dumpState("i = keyState ^ keyInput;", "i", i);

    // Increment the vertical counter for each pin that differs from the steady state, 
    // reset all other vertical counters. 
    // These two lines actually implement this increment operation with handling of 
    // carry from ct0 to ct1. Read the walkthrough to see this in action! 
    ct0 = ~( ct0 & i ); 
    dumpState("ct0 = ~( ct0 & i );", "ct0", ct0);
    ct1 = ct0 ^ (ct1 & i); 
    dumpState("ct1 = ct0 ^ (ct1 & i);", "ct1", ct1);

    // Detect which counters have rolled over - these correspond to the pins that have 
    // seen 4 consecutive identical states that are the opposite of the steady state 
    i &= ct0 & ct1; 
    dumpState("i &= ct0 & ct1;", "i", i);

    // Toggle the steady state of those pins 
    keyState ^= i; 
    dumpState("keyState ^= i;", "keyState", keyState);

    // Mirror the new keys pressed into the variable where the main app can 
    // eventually consume them. 
    keyPress |= keyState & i; 
    dumpState("keyPress |= keyState & i;", "keyPress", keyPress);
} 

//-- main() -------------------------------------------------------------------

int main(int argc, char * argv[])
{
    if (argc <= 1)
    {
        printf( "Usage: DanniDebounceSimulator 1 0 1 1 0 1 0 ...\n");
        exit(0);
    }

    // Pre-register some variables (forcing their display order)
    lookup("ct1");
    lookup("ct0");
    lookup("i");
    lookup("keyState");
    lookup("keyPress");
    dumpLegend();

    for (int i=1; i

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
C:\c[i386_vc]>cl sim.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

sim.c
sim.c(18) : error C2057: expected constant expression
sim.c(18) : error C2466: cannot allocate an array of constant size 0
sim.c(53) : error C2275: 'uint8_t' : illegal use of this type as an expression
        sim.c(7) : see declaration of 'uint8_t'
sim.c(53) : error C2146: syntax error : missing ';' before identifier 'lsb'
sim.c(53) : error C2065: 'lsb' : undeclared identifier
sim.c(54) : error C2065: 'lsb' : undeclared identifier
sim.c(61) : error C2143: syntax error : missing ';' before 'type'
sim.c(62) : error C2065: 'i' : undeclared identifier
sim.c(62) : error C2065: 'i' : undeclared identifier
sim.c(64) : error C2065: 'i' : undeclared identifier
sim.c(65) : error C2065: 'i' : undeclared identifier
sim.c(130) : error C2143: syntax error : missing ';' before 'type'
sim.c(130) : error C2143: syntax error : missing ';' before 'type'
sim.c(130) : error C2143: syntax error : missing ')' before 'type'
sim.c(130) : error C2143: syntax error : missing ';' before 'type'
sim.c(130) : error C2065: 'i' : undeclared identifier
sim.c(130) : warning C4552: '<' : operator has no effect; expected operator with side-effect
sim.c(130) : error C2065: 'i' : undeclared identifier
sim.c(130) : error C2059: syntax error : ')'
sim.c(131) : error C2143: syntax error : missing ';' before '{'
sim.c(132) : error C2065: 'i' : undeclared identifier

You didn't say it was C++ ;-)

C:\c[i386_vc]>ren sim.c sim.cpp

C:\c[i386_vc]>cl sim.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

sim.cpp
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:sim.exe
sim.obj

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh, crap. I thought I hacked away all C++ specifics but evidently not. See? That's why it makes sens to you all to switch to the somewhat less ancient language. :wink:

To me it looks like line 8 is

SymtabEntry symtab[SYMTAB_SIZE];

which is conistent with the error message. But why does it not treat SMTAB_SIZE as a "constant expression".. Does your compiler have a std99 option, and will it work better then? If not my "const uint8_t"s needs to be replaced by those pesky-ugly #define's.

Other errors also look really strange - some of them also looks like std99 not supported (e.g. not allowing declaration of loop variable in the for-loop per se).

EDIT: No your yucky compiler (and mine) does not have "C std99" because they are both from Microdaft..

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's a better one [attached].

And the proof that it is better:

C:\Danni>dir
 Volume in drive C has no label.
 Volume Serial Number is 3A54-482A

 Directory of C:\Danni

2011-07-29  16:00              .
2011-07-29  16:00              ..
2011-07-29  15:58             3 715 DanniDebouceSimulator.c
               1 File(s)          3 715 bytes
               2 Dir(s)  45 680 943 104 bytes free

C:\Danni>cl DanniDebouceSimulator.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

DanniDebouceSimulator.c
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:DanniDebouceSimulator.exe
DanniDebouceSimulator.obj

C:\Danni>DanniDebouceSimulator 0 1 0 1 1 1 1 1

   STATEMENT                               ct1         ct0           i    keyState    keyPress

ROUND 0 with keyInput == 0
   i = keyState ^ keyInput;                                          0
   ct0 = ~( ct0 & i );                                   1
   ct1 = ct0 ^ (ct1 & i);                    1
   i &= ct0 & ct1;                                                   0
   keyState ^= i;                                                                0
   keyPress |= keyState & i;                                                                 0

ROUND 1 with keyInput == 1
   i = keyState ^ keyInput;                                          1
   ct0 = ~( ct0 & i );                                   0
   ct1 = ct0 ^ (ct1 & i);                    1
   i &= ct0 & ct1;                                                   0
   keyState ^= i;                                                                0
   keyPress |= keyState & i;                                                                 0

ROUND 2 with keyInput == 0
   i = keyState ^ keyInput;                                          0
   ct0 = ~( ct0 & i );                                   1
   ct1 = ct0 ^ (ct1 & i);                    1
   i &= ct0 & ct1;                                                   0
   keyState ^= i;                                                                0
   keyPress |= keyState & i;                                                                 0

ROUND 3 with keyInput == 1
   i = keyState ^ keyInput;                                          1
   ct0 = ~( ct0 & i );                                   0
   ct1 = ct0 ^ (ct1 & i);                    1
   i &= ct0 & ct1;                                                   0
   keyState ^= i;                                                                0
   keyPress |= keyState & i;                                                                 0

ROUND 4 with keyInput == 1
   i = keyState ^ keyInput;                                          1
   ct0 = ~( ct0 & i );                                   1
   ct1 = ct0 ^ (ct1 & i);                    0
   i &= ct0 & ct1;                                                   0
   keyState ^= i;                                                                0
   keyPress |= keyState & i;                                                                 0

ROUND 5 with keyInput == 1
   i = keyState ^ keyInput;                                          1
   ct0 = ~( ct0 & i );                                   0
   ct1 = ct0 ^ (ct1 & i);                    0
   i &= ct0 & ct1;                                                   0
   keyState ^= i;                                                                0
   keyPress |= keyState & i;                                                                 0

ROUND 6 with keyInput == 1
   i = keyState ^ keyInput;                                          1
   ct0 = ~( ct0 & i );                                   1
   ct1 = ct0 ^ (ct1 & i);                    1
   i &= ct0 & ct1;                                                   1
   keyState ^= i;                                                                1
   keyPress |= keyState & i;                                                                 1

ROUND 7 with keyInput == 1
   i = keyState ^ keyInput;                                          0
   ct0 = ~( ct0 & i );                                   1
   ct1 = ct0 ^ (ct1 & i);                    1
   i &= ct0 & ct1;                                                   0
   keyState ^= i;                                                                1
   keyPress |= keyState & i;                                                                 1

C:\Danni>

Attachment(s): 

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Does your compiler have a std99 option,

Get real!

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved. 

There official line on C99 support is "it's far more important for us to concentrate on C++ support than C99" - so, no, M$ are stuck back in the last millenium.

EDIT: just realised you made the same point:

Quote:
EDIT: No your yucky compiler (and mine) does not have "C std99" because they are both from Microdaft..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If I was really to do it in C++, then of-course I would have something like (very sketchy, I'm just vomiting something C++ lookalike here):

class Debouncer
{
   ...
public:
   Debounce()
   {
      T i;
      i = steadyState ^ GetPinState();
      .
      .
      .
      switchesMade |= steadyState & i;
   }

protected:
   virtual T GetPinState() = 0;

private:
   T steadyState;
   T switchesMade;

   T ct1, ct0;
}

class MyDebouncer : public Debouncer
{
protected:
   uint8_t GetPinState()
   {
      .
      .
      .
   }
}

void main()
{
   MyDebouncer debouncer;


   while (1)
   {
      if (SysTick::Tick())
      {
         debouncer.Debounce();
      }


      if (debouncer.SwitchMade(1)
      {
         .
         .
         .
      }
   }
}

Then you just go with the flow from there: Make your systick a non-preemtive RTOS. Any class that implements the Runnable "interface" (in C++ "interfaces" are completely abstract classes (i.e. no runnable implementation at all, all methods are pure virtual) and "implementors of the interface" simply inherit that abstract interface class and implement the virtual functions). Anything that implementes Runnable can register in the SysTick class that now becomes a rudimentary RTOS or a simple Scheduler. At registration the interval between the runs is specified.

So for my debouncer:

   scheduler.Register(debouncer, 5);
   scheduler.Register(rtc, 1000);

Makes your eyes go wet, huh.. Why not dump C and come on over? :wink:

No, in reality, on an AVR using avr-gcc, I would prolly avoid virtual functions since the v-table is in RAM - not in flash.

For e.g. a Cortex-M3 this would probably come out different.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's gross. :yuck:

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." - Marcus Aurelius               

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I'm in
mode, I'll take issue with your assertion...

Quote:
in C++ "interfaces" are completely abstract classes (i.e. no runnable implementation at all, all methods are pure virtual)
It is quite feasible for an ABC to implement a default method which must be overridden, without being pure virtual. Now, in C#, you are quite correct. However, in C# an interface is a somewhat different beast to a C++ abstract base class.

--greg
Still learning, don't shout at me, educate me.
Starting the fire is easy; the hardest part is learning how to keep the flame!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dear Gregory!

I am not talking about C++ putting any requirements on an abstract class that it must only have pure virtual methods.

I am saying that the concept of an interface (which is .. uhmm .. an interface .. uhmm .. as in only interface, and no implementation) and you want to let classes adhere to that interface, and you want other classes to deal with objects adhering to that interface w/o taking anything else in the class into consideration THEN the idiom that you should use is a completely abstract base class.

So, while there is no technical support in C++ to help me enforce it, only a completely abstract base class is an "interface".

Yes, this will mimick interfaces as they are done in e.g. C#.

Aside: When I talk C++ with people, I generally advice to stay away from multiple inheritance, apart from when they inherit interfaces according to my definition above.

Most other things you accomplish with multiple inheritance can, IMO, be done in other ways that are far more suitable. Generally speaking, when OO "took off" the first time it was very much about inheritance. It took a long time, and a second "wave of OO" before it was realized that there where other ways. Key saying: "Favor composition over inheritance". Not trying to advocate to use patterns a la GoF all the time, but studying what they cataloged illustrates this principle of favoring composition.

Since nothing above is an absolute statement of how C++ is defined, but rather my opinions on how it should be used, you are free to disagree. :D

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
you are free to disagree
Thanks.
I don't claim to have all the answers. In fact, I don't actually claim to have any of them. However, I'm self taught in all the languages I use (which are plenty). I've learned, through trial and error and by reading (a great deal) and applying 'best practice', whenever I've seen it to be self evident. As noted in other threads, the entire oops paradigm was a tough hill for me to climb, but I made it, and have written some seriously complicated systems, which through refactoring numerous times, a child could now understand. (Well, perhaps thats a bit of an overstatement!) There are to my mind several 'fathers' of C++, and a number of guru's of oops design, of which the Group Of Four, feature high on my list.
I've forgotten the point I was trying to address...
Oh, yes. Composition over multiple inheritance. Both work. Both work well. Both have limitations. Choose whatever works for you at the time. You can always go back later and re-factor. (Assuming the 'management' will allow it).

As you've identified...

Quote:
Since nothing above is an absolute statement of how C++ is defined, but rather my opinions on how it should be used, you are free to disagree.

Short answer... Do what it takes to get the job done. Getting the job done, pay's my mortgage. :)

--greg
Still learning, don't shout at me, educate me.
Starting the fire is easy; the hardest part is learning how to keep the flame!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Oh, yes. Composition over multiple inheritance.

Even "favor composition over inheritance" (w/o the "multiple"). There was a time when some OO folks actually "assembled a car" by inheriting a chassis, an engine, a trannie and four wheels.

That's gross!

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
That's gross!
Well... as I 'still' use multiple inheritance, when I need a quick and dirty object which supplies multiple(objects) functionality, particularly when a singleton is called for. You'll just have to forgive (or forget) me :)

I remember back in the early eighties writing date handling routines and thinking 'this aint gunna work in twenty years... ah, sod em', it'll be somebody else's problem by then'

Doh!!

--greg
Still learning, don't shout at me, educate me.
Starting the fire is easy; the hardest part is learning how to keep the flame!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was talking about Johan vomiting something... That's gross, Yes?

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." - Marcus Aurelius               

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes Larry, thats gross.

--greg
Still learning, don't shout at me, educate me.
Starting the fire is easy; the hardest part is learning how to keep the flame!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could also add a callback to the debouncer class/function.

The fun you can have when an application not written by you uses callbacks all over the place and you try to find out how a RF packet received one the lowest layer bubbles up, layer by layer, all chained up with call-backs setup at initialisation time. Eclipse's very handy F3 shortcut key is useless then.