How to Detect Two Buttons Are Simultaneously True using Danni Debounce

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

I'm attempting to develop a C program for the ATMega8 that increments/decrements a value onto a LCD screen using two (increment and decrement) pushbuttons. For debouncing, I'm using the standard Danni Debounce code. One of my requirements is to reset the LCD value back to zero when both button inputs are true at any time. I'm having trouble achieving this, and I have a feeling that it stems from the Danni Debounce code (or at least my understanding of it).

 

My attempt for simultaneous button press sensing is as follows:

 

	if(get_key_press(1<<PB1))
	{
		SendCommand(0x01); //clear display
		++LCDValueInt; //inc LCDValueInt
		sprintf(LCDValueString, "%d", LCDValueInt); //convert LCDValueInt to string
		SendAString(LCDValueString); //send string to LCD
	}
	
	if(get_key_press(1<<PB2))
	{
		SendCommand(0x01); //clear display
		--LCDValueInt; //decrement LCDValueInt
		sprintf(LCDValueString, "%d", LCDValueInt); //convert LCDValueInt to string
		SendAString(LCDValueString); //send string to LCD
	}
	
	if((get_key_press(1<<PB1)) && (get_key_press(1<<PB2)))
	{
	    SendCommand(0x01); //clear display
	    LCDValueInt = 0; //reset LCD to zero
	    sprintf(LCDValueString, "%d", LCDValueInt); //convert LCDValueInt to string
	    SendAString(LCDValueString); //send string to LCD
	}

For testing, I press and hold the increment button down, and then press and hold the decrement button. The code above will increment the LCD value, but then decrement the value when I press the decrement button instead of resetting the value to zero.

 

Maybe get_key_press() is the incorrect value for checking if the button is being held down. Maybe there's some value that maintains true while the button remains pressed, but returns to zero when the button is depressed. 

 

Any thoughts on how to achieve the LCD value reset using two simultaneously-pressed, Danni debounced buttons?

 

 

The Danni Debounce code I'm using, for reference

......
ISR(TIMER1_COMPA_vect) ////// debounce interrupt code
{
  static uint8_t ct0 = 0xFF, ct1 = 0xFF;	// 8 * 2bit counters
  uint8_t i;

  i = ~PINB;				// read keys (low active)
  i ^= key_state;			// key changed ?
  ct0 = ~( ct0 & i );			// reset or count ct0
  ct1 = ct0 ^ (ct1 & i);		// reset or count ct1
  i &= ct0 & ct1;			// count until roll over ?
  key_state ^= i;			// then toggle debounced state
  key_press |= key_state & i;
}

uint8_t get_key_press( uint8_t key_mask )
{
	ATOMIC_BLOCK(ATOMIC_FORCEON) // read and clear atomic !
	{		
		key_mask &= key_press;		// read key(s)
		key_press ^= key_mask;		// clear key(s)
	}
	return key_mask;
}......

 

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

Principally speaking, Dannis debouncer can not detect keys pressed at the same time - and store that information for later. There is no timestamp information stored.

 

Heres one (weird?) idea

1) Detect that "KEY1" has been pressed and "KEY2" has been pressed, and then, qiuckly

2) Read the raw input pins and if they both are in the pressed state then you have a "simultaneously pressed" situation.

 

Mind you, if the switches/keys are on different I/O ports then it is theoretically impossible to make a foolproof mechanism for detecting simultaneously pressed keys. Since you can not read two registers at the same time there will be some time glitch between the reads where port stat might change. Perhaaps less than a microsecond, but it is possible.. ;-)

 

EDIT: Reformulated ingress to my idea.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

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

Last Edited: Fri. Jul 14, 2017 - 10:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm no expert on this code but as the thing passed to get_key_press() is a bit mask why can't you simply:

if (get_key_press((1 << PB1) | (1 << PB2)) == ((1 << PB1) | (1 << PB2)) { ...

then?

 

(may have got that wrong - but hopefully the idea is clear?)

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

JohanEkdahl wrote:
it is theoretically impossible to make a foolproof mechanism for detecting simultaneously pressed keys.

I think you would have to "debounce" the condition that both keys are pressed - rather than each key individually ... ?

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

clawson wrote:

I'm no expert on this code but as the thing passed to get_key_press() is a bit mask why can't you simply:

if (get_key_press((1 << PB1) | (1 << PB2)) == ((1 << PB1) | (1 << PB2)) { ...

then?

 

(may have got that wrong - but hopefully the idea is clear?)

 

clawson,

 

This is exactly what I needed. An interesting find, though:

 

When this condition is separate from the other 'if' statements in the main For loop, it resets back to zero if both buttons are pressed, but I get strange behavior from the inc/dec buttons - The buttons require multiple sporadic presses before they they inc./dec. the LCD value. They don't inc./dec. as smoothly as without the additional resetting 'if' statement.

 

See the code below:

 

for(;;)
{
	if(get_key_press(1<<PB1))
	{
		SendCommand(0x01); //clear display
		++LCDValueInt;
		sprintf(LCDValueString, "%d", LCDValueInt);
		SendAString(LCDValueString);
	}
	
	if(get_key_press(1<<PB2))
	{
		SendCommand(0x01); //clear display
		--LCDValueInt;
		sprintf(LCDValueString, "%d", LCDValueInt);
		SendAString(LCDValueString);
	}
	
	if(get_key_press((1<<PB1)|(1<<PB2)) == ((PINB & (1<<PB1|1<<PB2))))
	{	
		SendCommand(0x01); //clear display
		LCDValueInt = 0;
		sprintf(LCDValueString, "%d", LCDValueInt);
		SendAString(LCDValueString);
	}
}

Any thoughts as to why this is? Mostly just out of curiousity. Perhaps a buffering/memory issue with the debounce code?

 

 

 

What I did do what put each resetting conditional if statement into each inc./dec. if statement. This seemed to work just fine, and the values inc./dec. as smoothly as before, as well as resetting when both buttons are pressed.

 

for(;;)
{
	if(get_key_press(1<<PB1))
	{
		SendCommand(0x01); //clear display
		++LCDValueInt;
		sprintf(LCDValueString, "%d", LCDValueInt);
		SendAString(LCDValueString);
		
		if(get_key_press((1<<PB1)|(1<<PB2)) == ((PINB & (1<<PB1|1<<PB2))))
		{
			SendCommand(0x01); //clear display
			LCDValueInt = 0;
			sprintf(LCDValueString, "%d", LCDValueInt);
			SendAString(LCDValueString);
		}
	}
	
	if(get_key_press(1<<PB2))
	{
		SendCommand(0x01); //clear display
		--LCDValueInt;
		sprintf(LCDValueString, "%d", LCDValueInt);
		SendAString(LCDValueString);
		
		if(get_key_press((1<<PB1)|(1<<PB2)) == ((PINB & (1<<PB1|1<<PB2))))
		{
			SendCommand(0x01); //clear display
			LCDValueInt = 0;
			sprintf(LCDValueString, "%d", LCDValueInt);
			SendAString(LCDValueString);
		}
	}

It might be sloppy, but it seems to work OK so far.

 

 

Thanks everyone for the help!

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

You should clear global var key_press after you detect a key?
.
MG

I don't know why I'm still doing this hobby

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

MicroGyro wrote:
You should clear global var key_press after you detect a key?

get_key_press() does that:

uint8_t get_key_press( uint8_t key_mask )
{
	ATOMIC_BLOCK(ATOMIC_FORCEON) // read and clear atomic !
	{		
		key_mask &= key_press;		// read key(s)
		key_press ^= key_mask;		// clear key(s)
	}
	return key_mask;
}

 

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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

JohanEkdahl wrote:
get_key_press() does that:

Sigh. I feel old.
.
MG

I don't know why I'm still doing this hobby

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

justin2021 wrote:
It might be sloppy, but it seems to work OK so far.

I'm surprised at that because you follow one get_key_press() with another on the same key.

As JohanEkdahl wrote: get_key_press() clears the global var key_press after you detect a key and that behaviour causes the original code to fail.

 

if(get_key_press(1<<PB2))
{
    ...
    if(get_key_press((1<<PB1)|(1<<PB2)) == ((PINB & (1<<PB1|1<<PB2))))

This reading of the raw port pin is a bit of a code smell and takes [me] a few read through to determine exactly what's going on.

 

You could avoid the messy code by reading the key presses into a local variable like this: (There's a code design rule called "Don't repeat Yourself" (DRY) https://en.wikipedia.org/wiki/Don%27t_repeat_yourself I've tried to follow that here also).

 

for(;;)
{
    uint8_t key_press = get_key_press((1<<PB1)|(1<<PB2));
    
    if((key_press & (1<<PB1)) && (key_press & (1<<PB2)))
    {
        LCDValueInt = 0;
        reprintLCDValueInt();
    }
    else if(key_press & (1<<PB1))
    {
        ++LCDValueInt;
        reprintLCDValueInt();
    }
    else if(key_press & (1<<PB2))
    {
        --LCDValueInt;
        reprintLCDValueInt();
    }
}

void reprintLCDValueInt (void)
{
    SendCommand(0x01); //clear display
    sprintf(LCDValueString, "%d", LCDValueInt);
    SendAString(LCDValueString);
}