Switch Debounce Code

Go To Last Post
67 posts / 0 new

Pages

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

I changed my mind and did the cancel instead (deleted the post).

Thanks anyway ...

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

danni wrote:
Restart00 wrote:
Agreed - yes, you frequently have a lot of switches, but you can usually produce a unique interrupt for each switch, and it's not so hard to keep track of several timeouts simultaneously.

That's multiplying the effort without any benefit.

A typical human need >300ms to press a key.
So some further ms delay was not noticeable.

Peter

I agree it won't make any difference in a lot of situations, but in some situations minimizing the latency could be important.

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

That would be a good test program to post here. Switch press timing. My personal research shows 100ms response time as the threshold of 'oh crap. I pushed the button and the light didn't come on'. So I determined that a program that turns on a light in response to a switch actuation needs to run at least that fast.

Imagecraft compiler user

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

In most situations you want to react on a press without any delay, the debounce is so you only count it as one press!

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

For the risk of being banned I take my chances and bumb a Debounce thread twice..

First :
Tank you all supporting and sorted out my hardware issues. I was so close.. so close to raise the capitulation flag.

So I'm searching for some code practices that involves button presses and it's outcome which is what is discussed in this tread.
Another reason for choosing this tread is to give some examples how you could use above posted code by danni for newbies like me.(or how you shouldn't)

So I have a output that will always follow the different inputs. I have called it p_enb.
My code is follow:
Button active when low
Output active when low
A.)

...
//inputs
#define In_1B PB0
#define In_2A PA7
#define In_2B PA6
...

// Outputs
#define Out_1B PB1
#define Out_2A PB2
#define Out_2B PB3
....
#define Out_P_enb PC2

// Debounce KEY MASK
#define KEY_PIN   ((PINB & 1<<In_1B) | (PINA & 1<<In_2A) | (PINA & 1<<In_2B))  
					 
.....

uint8_t p_enb ;    // State of pressure enable
int main(void)
{
.....
	sei();

	while(1)
	{
		// toggled output
		if (get_key_press(1<<In_1B))
		{
                        PORTB ^=1<<Out_1B;
                        p_enb ^= 1<<In_1B;
		}
        ...
		// another toggled output
		....
		//  monentary output
		if ((key_state & 1<< In_2A) == 1<<In_2A)
		{
                        PORTB &= ~(1<<Out_2A);
                        p_enb &= ~(1<<In_2A);
		}
		else 
		{
                        PORTB |=1<<Out_2A;
                        p_enb |=1<<In_2A;
		}

        // on line if statement
        PORTC = (p_enb == 0) ? (PORTC |= 1<<Out_P_enb )
	                     : (PORTC &= ~(1<<Out_P_enb));  		
	}
}

Is there a chance that p_enb will be shifted to it's inputs done by external disturbence. So when you press for toggle on a function .. p_enb will do the opposite.
In that case would it help if do some sort of checksum by introduce another variable p_enb2. If they diverse reset what have to be reset

Like ...
B.)

....
uint8_t p_enb1, p_enb2 ;    // State of pressure enable
....
int main(void)
{
.....
	sei();
	while(1)
	{
		// toggled output
		if (get_key_press(1<<In_1B))
		{
			PORTB ^=1<<Out_1B;
			p_enb1 ^= 1<<In_1B;
			p_enb2 ^= 1<<In_1B;
		}

        //  monentary output
		if ((key_state & 1<< In_2A) == 1<<In_2A)
		{
			PORTB |= (1<<Out_2A);
			p_enb1 |= (1<<In_2A);
     		        p_enb2 |= (1<<In_2A);
		}
		else 
		{
			PORTB &= ~(1<<Out_2A);
			p_enb1 &= ~(1<<In_2A);
			p_enb2 &= ~(1<<In_2A);			
		}
		
		if (p_enb1 != p_enb2){
			// reset what have to be reset
		}
		else
		PORTC = (p_enb1  == 0) ? (PORTC &= ~(1<<Out_P_enb))
                                       : (PORTC |= (1<<Out_P_enb));		
	}  
}

Would be interested to hear how you should do in this case.

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

I can't help it .. I'm going round and round in circles here .. On the hardware that I'm working on I have jumper that I can choose if button is active low or high. But I can't get my head around this on how to implement that.

Anyway I have started from scratchs again and are copying and pasting from this example.

/************************************************************************/
/*                                                                      */
/*                      Debouncing 8 Keys                               */
/*                      Sampling 4 Times                                */
/*                      With Repeat Function                            */
/*                                                                      */
/*              Author: Peter Dannegger                                 */
/*                      danni@specs.de                                  */
/*                                                                      */
/************************************************************************/
 
#include 
#include 
#include 
#include 
 
#ifndef F_CPU
#define F_CPU           1000000                   // processor clock frequency
#endif
 
#define KEY_PORT        PORTA
#define KEY_PIN         PINA
#define KEY0            7

#define ALL_KEYS        ( 1<<KEY0 )
 
#define REPEAT_MASK     ( 1<<KEY0 )       // repeat: key1, key2
#define REPEAT_START    50                        // after 500ms
#define REPEAT_NEXT     20                        // every 200ms
 
#define LED_DDR         DDRA
#define LED_PORT        PORTA
#define LED0            0
#define LED1            3
#define LED2            4

#define LED1_ON ( PORTA |= 0x01 )
#define LED1_OFF ( PORTA &= ~0x01 )
#define LED2_ON ( PORTA |= 0x08 )
#define LED2_OFF ( PORTA &= ~0x08 )
#define LED3_ON ( PORTA |= 0x10 )
#define LED3_OFF ( PORTA &= ~0x10 )
 


volatile uint8_t key_state;                                // debounced and inverted key state:
                                                  // bit = 1: key pressed
volatile uint8_t key_press;                                // key press detect
 
volatile uint8_t key_rpt;                                  // key long press and repeat
 
 
ISR( TIMER0_OVF0_vect )                            // every 10ms
{
  static uint8_t ct0, ct1, rpt;
  uint8_t i;
 
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
 
  i = key_state ^ ~KEY_PIN;                       // key changed ?
  ct0 = ~( ct0 & i );                             // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
  i &= ct0 & ct1;                                 // count until roll over ?
  key_state ^= i;                                 // then toggle debounced state
  key_press |= key_state & i;                     // 0->1: key press detect
 
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
     rpt = REPEAT_START;                          // start delay
  if( --rpt == 0 ){
    rpt = REPEAT_NEXT;                            // repeat delay
    key_rpt |= key_state & REPEAT_MASK;
  }
}
 
///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed. Each pressed key is reported
// only once
//
uint8_t get_key_press( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_press;                          // read key(s)
  key_press ^= key_mask;                          // clear key(s)
  sei();
  return key_mask;
}
 
///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed long enough such that the
// key repeat functionality kicks in. After a small setup delay
// the key is reported beeing pressed in subsequent calls
// to this function. This simulates the user repeadiately
// pressing and releasing the key.
//
uint8_t get_key_rpt( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_rpt;                            // read key(s)
  key_rpt ^= key_mask;                            // clear key(s)
  sei();
  return key_mask;
}
 
///////////////////////////////////////////////////////////////////
//
uint8_t get_key_short( uint8_t key_mask )
{
  cli();                                          // read key state and key press atomic !
  return get_key_press( ~key_state & key_mask );
}
 
///////////////////////////////////////////////////////////////////
//
uint8_t get_key_long( uint8_t key_mask )
{
  return get_key_press( get_key_rpt( key_mask ));
}
 
int main( void )
{
  DDRA = 0x19;

  int count=0;
 
  TCCR0 = (1<<CS02)|(1<<CS00);			// divide by 1024
  TIMSK = 1<<TOIE0;				// enable timer interrupt
 

  sei();
 
  for(;;) {                                       // main loop
                                                  // single press
  /* 
   if( get_key_press( 1<<KEY0 ))
	{
      if(count<=3)
	  count++;
	}
   */                                                   // release after short press: task 1
                                              // long press: task 2
 
   if( get_key_short( 1<<KEY0 ))
	{
      if(count<=3)
	  count++;
	}
 
    if( get_key_long( 1<<KEY0 ))
    {
	  	LED1_OFF;
		LED2_OFF;
		LED3_OFF;
		count = 1;
	}

 	switch(count)
	{
		case 1: LED1_OFF;
				LED2_OFF;
				LED3_OFF;
				break;
		case 2: LED2_OFF;
				LED3_OFF;
				LED1_ON;
				break;
		case 3: LED3_OFF;
				LED1_OFF;   
				LED2_ON;
				break;
		case 4: LED1_OFF;
				LED2_OFF;
				LED3_ON; 
				break;  
	}
				
				
	 // single press and repeat
/*
   if( get_key_press( 1<<KEY0 ) || get_key_rpt( 1<<KEY0 )){
      uint8_t i = LED_PORT;
 
      i = (i & 0x07) | ((i << 1) & 0xF0);
      if( i < 0xF0 )
        i |= 0x08;
      count=0;
   } 
*/
  }
}

From what I have read the buttons is active Low. So if I have buttons that is active high I must change

  i = key_state ^ ~KEY_PIN;                       // key changed ?

to 

  i = key_state ^ KEY_PIN;                       // key changed ?

?? (yes/no)

In my struggle to get the button to work in both states. Light the led if longer press and toggle the led if just a short press.. It would help me if some more yes/no question could be answered.

As stated in the example

volatile uint8_t key_state;                                // debounced and inverted key state:
                                                  // bit = 1: key pressed

That's true ? It's not the other way around ... (Yes = when bit goes to 1 the key is pressed : No = It goes from 1 to 0 when pressed)

With this ...

   if( get_key_short( 1<<KEY0 ))
	{
      toggle the led

	}

It should only toggle the led with short press. If I press longer it will not toggle the led ? Yes/No

If working with button active high and longer press I must also invert the REPEAT_MASK ?

  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
     rpt = REPEAT_START;                          // start delay
  if( --rpt == 0 ){
    rpt = REPEAT_NEXT;                            // repeat delay
    key_rpt |= key_state & REPEAT_MASK;

must be changed to 

  if( (key_state & ~REPEAT_MASK) == 0 )            // check repeat function
     rpt = ~REPEAT_START;                          // start delay
  if( --rpt == 0 ){
    rpt = ~REPEAT_NEXT;                            // repeat delay
    key_rpt |= key_state & ~REPEAT_MASK;

Yes /No

I have gone from fiddling with the avr on the hardware. Later I have removed it wiring out all inputs and outputs to Arduino UNO just to get the Serial.println to get a glim’s of what is going on. The debugged results doesn't match what is quoted in the big example.

To push this further it would be nice if I could get some answer .. yes , yes , no ,no etc ...

Under mean time I will remove all the hardware and just go with UNO and one button on breadboard.
My first experience with mcu have gone from bad -> worse -> worst -> ….

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

Quote:

From what I have read the buttons is active Low. So if I have buttons that is active high I must change

Code:

i = key_state ^ ~KEY_PIN; // key changed ?

to

i = key_state ^ KEY_PIN; // key changed ?

?? (yes/no)


Yes.

Quote:

As stated in the example

Code:

volatile uint8_t key_state; // debounced and inverted key state:
// bit = 1: key pressed

That's true ?


Yes, given that the switchwes are active low and the actual read was

  i = key_state ^ ~KEY_PIN;

If the switches are active high, and the actual read thus is changed to

  i = key_state ^ KEY_PIN;

the comment is misleading. Remove the "and debounced"
for a better comment.

Quote:

With this ...

Code:

if( get_key_short( 1<<KEY0 ))
{
toggle the led

}

It should only toggle the led with short press. If I press longer it will not toggle the led ? Yes/No


As I read the code, at a glanse, it will trigger also for long presses. I might be wrong..
Quote:

If working with button active high and longer press I must also invert the REPEAT_MASK ?

No. One of the pretty things with Dannis code is that there is only one point in the code where you actually need to handle if switches are active high or active low. It is at the reading of the port. If they are active low then you invert when reading, if active high then you do not invert. From there on all bits are interpreted as 1 meaning pressed/active and 0 meaning released/non-active.

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]

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

And don't forget the external pull-down resistors if you are using active high. With active low you can use the internal pull-up resistors. What board are you using? Do you have a link or a schematic?

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

JohanEkdahl wrote:
Quote:

If working with button active high and longer press I must also invert the REPEAT_MASK ?


No. One of the pretty things with Dannis code is that there is only one point in the code where you actually need to handle if switches are active high or active low. It is at the reading of the port. If they are active low then you invert when reading, if active high then you do not invert. From there on all bits are interpreted as 1 meaning pressed/active and 0 meaning released/non-active.

REPEAT_MASK is a port reading definition. So my guesses it should be inverted if button is active high. I have tried to get a grip of this but when I have become unsure of all other things it becomes even harder.

So the code should be changed to ...

 if( (key_state & ~REPEAT_MASK) == 0 )            // check repeat function
     rpt = REPEAT_START;                          // start delay
  if( --rpt == 0 ){
    rpt = REPEAT_NEXT;                            // repeat delay
    key_rpt |= key_state & ~REPEAT_MASK; 

If butt is active high
I can't test it at the moment ...

Quote:

With this ...

Code:

if( get_key_short( 1<<KEY0 ))
{
toggle the led

}

It should only toggle the led with short press. If I press longer it will not toggle the led ? Yes/No

As I read the code, at a glanse, it will trigger also for long presses. I might be wrong..

My findings also verify that it's also catches by long press.

snigelen wrote:
And don't forget the external pull-down resistors if you are using active high. With active low you can use the internal pull-up resistors. What board are you using? Do you have a link or a schematic?

The schematics is in my first post in this thread -> https://www.avrfreaks.net/index.p...
The PEEL20CG10A is replaced with tiny40.

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

Quote:

REPEAT_MASK is a port reading definition.

Really? In Dannis code, which you posted above it is

#define REPEAT_MASK     ( 1<<KEY0 )

and KEY0 in turn is just

#define KEY0            7

so KEY_MASK actually gets to be a bit mask for the bits where repeat-detection should be done. All this as I read the code. No port reading there as far as I can see.

Where do you see the port-reading?

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]

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

pernils wrote:
REPEAT_MASK is a port reading definition. So my guesses it should be inverted if button is active high.

Then you guess wrong.
After the ~KEY_PIN operation all further logic rely on active high.

pernils wrote:
if( get_key_short( 1<<KEY0 ))

get_key_short must always be used in conjunction with get_key_long !
Otherwise it don't work.

Also both can not be used with get_key_press, get_key_rpt together (for the same key or in the same context).
For different keys or in different context it is still possible.

Peter

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

(finally some time over) ...

Heh .. The original author ... Peter cheers ...

Thanks to you guys for pushing me back on track.

(Just for summaries for other readers ..)

First error ...

I'm collecting KEY_PINS from different ports.

Like :

#define KEY_PIN     ((PINC & 1<<In_1B) | (PINC & 1<<In_2A) | (PIND & 1<< In_2B))

In the hurry I was just copy and paste the same in REPEAT_MASK. (thicker glasses maybe..)

(Statement from a newbie.. Correct me if I'm wrong)
The above define is valid so long you are ORing pins with difference bits. So ..

#define KEY_PIN     ((PINC & 1<<PORTC1) | (PINC & 1<<PORTC2) | (PIND & 1<< PORTD1))

Will give you problem. PORTC1 and PORTD1 is referring to the same bit. In this case you must shift on of those to an "empty" bit. I don't know how at the moment.

Second Error ....

I didn't take my time to read the code ...
get_key_long and get_key_short is calling get_key_press. In get_key_press the variable holding the pressed key (key_press) is cleared.

So for be able to react different on short key press and long key press.
Remove key_press clearing and clear it you self in your loop where you are are calling the functions.

......
uint8_t get_key_press( uint8_t key_mask )
{
    cli();                    // read and clear atomic ! 

    key_mask &= key_press;    // read key(s)    .... key_press is denounced and ready by the ISR routine
    //key_press ^= key_mask;    // clear key(s)

    sei();                    // enable interrupts
    return key_mask;


....

void main()
{
    if (get_key_long(1<<In_1B))
    {
        PORTD ^= 1<<Out_2B;
        key_press ^= 1<<In_1B;    // clear key(s)
    }

    if (get_key_short(1<<In_1B))
    {
        PORTB ^= 1<<Out_3A;
        key_press ^= 1<<In_1B;    // clear key(s)
    }
}

Finally I can move back from arduino to my main mcu and pcb ... Hmm will be interesting to see where I get stuck next time.

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

Quote:

PORTC1 and PORTD1 is referring to the same bit. In this case you must shift on of those to an "empty" bit. I don't know how at the moment.

Correct.

I thin you are taking the good approach here. At one place in the code you "translate" the raw inputs from digital pins to an internal representation. At that stage you OR, SHIFT and complement the pin values in any way you need.

From there on nothing should be altered in the debouncing code.

If it makes sense: If this was C++ one could write a generic debouncer, that had one pure virtual function GetKeyState() that you'd have to override. The rest of the class should need no alteration.

Re the short and long stuff. I have not analyzed this in detail, but I speculate that if a detected short key press will lead to clearing the corresponding bit in key_press, then it will not be seen as a long key press ever.

A fundamental problem here is that if you want to detect both short and long presses, and do not wat the "short action" if a long press was made then the short detection must in some way determine that a long press is not "in progress". Put another way: You can not determine that a key press was short until you can positively etermine that it was not a long key press.

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]

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

Thank you dear Peter,

I am using your debounce code its very useful and working very well.Thank you dear thank you. :D

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

Can someone here teach me how to make a push button work for port B pin 10 of arduino function as a start/stop button and a push button for port B pin 9 of arduino function as a reset button.

 

I need to use atmel studio atmega 328p and also in C language.

 

I attach also my schematic.

Attachment(s): 

Last Edited: Thu. Jul 13, 2017 - 05:39 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hakugen STOP POSTING ALL OVER THE PLACE! You are risking being banned. Just stay on your thread

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Pages

Topic locked