My approach to software debouncer NOW SWITCH HOLDING ?

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

Hi :)))

Once again I made some changes to my software debouncer code. Now I wanted to add a function which would react for a push button (aproximately 0,5 s).
It is quite necessary for many projects. Forgive me if I haven`t noticed similar subject but with regret I couldn`t find any of them here so I have decided to write this post and share your opinion.

The main thing what program should do is to react for a single push then incerease PORTB++.
Second thing happens when we hold a button for aprox. 0.5s then PORTB is shifting.

Probably you will better know what I mean from the code ;) ;) ;)

I made tests on atmega8
1000000 [Hz]
Everything is working fine :)
What do you think about this code ??
How you usually make it ??

Thanks for any reamarks and help :)

Have you noticed that from some time I`m not asking you for help but for suggestions ;) ;) ;)
Well maybe I`m wrong but I have this nice feeling ;)

Ok here is the code:

#include 
#include 
#define MAX_CHECKS 6// number of checks before a switch is debounced
#define MAX_HOLD 32

/* Uninitialized global or static variables end up in the .bss section
which means that they equal 0 or null and you don`t have to assign them.
"Lucky for you you made this a BSS variable so it's guaranteed 
to be cleared to 0 anyway." */

//volatile unsigned char key_press; // here we have to use volatile
//Global variables:					// or "atomic access" function !!!
unsigned char key_press;          // "FAQ1: ISR/main variables MUST 
unsigned char key_state;          // be defined volatile"
unsigned char state[MAX_CHECKS];		   
unsigned char counter;	// uint8_t = unsigned char look avr library for 
					    // more info if you want to use this typedef
unsigned char key_hold_press;
unsigned char key_hold_state;
unsigned char counter_2;
unsigned char key_hold_button[MAX_HOLD];

ISR(TIMER0_OVF_vect)
{
	unsigned char i,j,k; //These wariables don`t equal zero cause
	//they are not global. Assign values before you use them !!!
	static unsigned char prev_state; //This value is seen outside 
									   //ISR and is equal 0 at start
	state[counter]= (~(((PIND & 1<<2)>>2)|((PINC & 1<<2)>>1))); //input
	counter++;
	j=0xff; //mask
	k=0x00; //mask						   
	for(i=0; i 1: key press detect
   if (counter>=MAX_CHECKS)
   {
    counter=0; //reset counter
   
   //hold_button
   j=0xff;
   key_hold_state&=~((key_state^prev_state) & key_state); 
   key_hold_button[counter_2]=key_state;
   counter_2++;
   for (i=0;i=MAX_HOLD)
   {
   	
   		counter_2=0;
	}
	
   }
} 

// Below is presented function called "atomic access".
// You don`t have to define key_press as volatile.
unsigned char get_key_press(unsigned char key_mask) 
{
	cli();
	key_mask &=key_press;
	key_press ^=key_mask;
	sei();
	return key_mask;
}

unsigned char get_key_hold(unsigned char key_mask) 
{
	cli();
	key_mask &=key_hold_press;
	key_hold_press ^=key_mask;
	sei();
	return key_mask;
}

int main(void)
{
	unsigned char n=0;

	TCCR0 = 1<<CS01; // enable timer interrupt 
	TIMSK = 1<<TOIE0;                     

	DDRB=0xff;		
   
	PORTC=0xff; // enable internal pulling-up remember that then 
	PORTD=0xff;	// negation is triggering signal 
  
	sei(); // gloabal interrupts enable

	for(;;)
		{
			if(get_key_press(1))
			{
				n++;
			}
			if(get_key_press(2))
			{	
				n--;
			}
			if(get_key_hold(1))
			{
			n=n<<1;
			}
			if(get_key_hold(2))
			{
			n=n>>1;
			}
			PORTB=n;
		}

Adam 8) :)

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

I haven't inspected the code, but it seems like a good idea.

I suppose if you extended this idea far enough, one push button could replace an entire keyboard :)

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

Your interrupt handler looks very big and long (lots of loops inside).
An interrupt handler should always be short and fast, especially, if you want to reuse it on other projects.

Maybe you should look on my small and powerful debouncer:

http://www.mikrocontroller.net/a...

It should be over 10 times faster and smaller than yours.
If you look on the generated assembler listing, you can see the big difference.

Also watch, that I use different functions for key press and short press detection.

In general a user want reaction on pressing a key.

But any key with duration detection can't do it.
It can only detect a short key press after releasing the key. Since it can't look into the future.

Peter

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

When I normaly make a debounce rutine where you need different states I do it with a counter in the interrupt rutine. It simply count up if pressed (and stop at 0xff)and down when not (stop at 0). Now you can ask if counter > $e0 then key i pressed. And if the test have been true for x times it's a long press

It sounds like a lot, but the main thing is that the interrupt rutine is small and fast (just a counter) the rest can be made outside.

Jens

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

Hi sorry for responding with such delay but I was brainstorming Peter`s debounce code.

Peter your code is magnicifent :) I don`t know if you remember but in my previous post I tried to discuss about your code:

https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=55185

Anyway I resigned from applying it to my project cause I was not able to understand what`s going on in the code.
I was able to see that this is a great code but I couldn`t understand it to the end.

// I would say that the same I have with music I just
// can hear it is good and great song but I can`t play it;)

Comming back to the topic:
I spent few days with this code and I would like to ask few additional questions:

As a proof of spending time with this code I would like to give you a code for the next ct3,ct3,ct4

ct2 = (ct0 & ct1)  ^ (ct2 & i);
ct3 = (ct0 & ct1 & ct2) ^ (ct3 & i);
ct4 = (ct0 & ct1 & ct2 & ct3) ^ (ct4 & i);

and so on... :)

Questions:

1) Peter why you use library it is said on this forum that it shouldn`t be used and should be replaced by library.
(Maybe cause you got used to old vectors and you know them by heart ??)

2) Your code looks like:

#define REPEAT_MASK	(1<<KEY1^1<<KEY2)

Wouldn`t be easier to write it like this?:

#define REPEAT_MASK	(1<<KEY1|1<<KEY2)

3) Why you use

typedef unsigned char	u8;
typedef signed short	s16;

I usually write usigned char or int before each variable?
Is it just for making life easier ?? ;)

4) Why you preload timer for 10ms ??
What is the reason for doing this ??

TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5);	// preload for 10ms

also I would like to ask for values for (u8) and (s16)??
Are they:
(u8=8, s16=16)
When I am using them do I have to write them in brackets like you did ??

als did you mean:

TCNT0 = (u8)(s16)-(XTAL / (1024 * 10e-3 + 0.5));	// preload for 10ms

Why you add 0.5 to this equation??
Why it looks like this ??

// Probably you are tired now with all this questions but I hope you will help me with few more please :)

5)

u8 get_key_short( u8 key_mask )
{
  cli();			// read key state and key press atomic !
  return get_key_press( ~key_state & key_mask );
}

What for is

cli();

in this function if we have cli(); inside get_key_press();??
I supose you forgot to delete it when rewriting this new function ??

6) Oh and do you think I can use

 i = key_state ^ KEY_PIN;	

waithout ~ ???
Then key_state should not be inverted.
Or as far as I am concern I should change it when active state is 0V. For example I can use internall pull-up.

Uff that`s all :)
I am so stubburn I suppose :)
But I feel so bad when I am using sb`s else code and don`t understand it.

Peter you are great,awesome,superb programmer.

Actually you are my Exor Master :) :) :)

Take Care

Sorry for any mistakes if they appeared in this text ;)

Adam

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

Ifryd wrote:

As a proof of spending time with this code I would like to give you a code for the next ct3,ct3,ct4

You can do it, but 4 times debouncing was fully sufficient on my experience also for very long bouncing keys.

Ifryd wrote:

1) Peter why you use

The code was old and the older WINAVR needed to include signal.h.
Today you should adapt it to the current WINAVR.

Ifryd wrote:

2) Your code looks like:

#define REPEAT_MASK	(1<<KEY1^1<<KEY2)

Wouldn`t be easier to write it like this?:

#define REPEAT_MASK	(1<<KEY1|1<<KEY2)

On a German keyboard ^ was easier to type instead |.
You can use both.

Ifryd wrote:

3) Why you use

typedef unsigned char	u8;
typedef signed short	s16;

I usually write usigned char or int before each variable?
Is it just for making life easier ?? ;)

Its for laziness.
I'm not a good typewriter writer.

Ifryd wrote:

4) Why you preload timer for 10ms ??
What is the reason for doing this ??

Its a good debouncing interval.
But its not critical (2ms ... 50ms).

Ifryd wrote:

TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5);	// preload for 10ms

also I would like to ask for values for (u8) and (s16)??

see your point 3)
Its made to cast float -> 8bit.

Ifryd wrote:

als did you mean:

TCNT0 = (u8)(s16)-(XTAL / (1024 * 10e-3 + 0.5));	// preload for 10ms

Why you add 0.5 to this equation??

Its rounding (nearest integer).

Ifryd wrote:

5)
u8 get_key_short( u8 key_mask )
{
  cli();			// read key state and key press atomic !
  return get_key_press( ~key_state & key_mask );
}

What for is

cli();

in this function if we have cli(); inside get_key_press();??
I supose you forgot to delete it when rewriting this new function ??

No, it must be done to make the whole function atomic!

Ifryd wrote:

6) Oh and do you think I can use

 i = key_state ^ KEY_PIN;	

waithout ~ ???
Then key_state should not be inverted.

Yes it should.
Otherwise the keys must be connected against VCC and separate pulldown resistors needed.

The press catching on the end need 0-1 transition, so the keys must be inverted if low active.

Naturally press catching on 1-0 transition can also be done, but need more code.

But I try often to use internal states as high = on, independend from the external pin level.

Peter

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

I may be wrong, but could not be used something like this?

counter = 0;
while(key_pressed)
{
counter++;
delay_ms(50);
}
if(counter>10)... //key held more then 0.5 sec
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ifryd wrote:
3) Why you use

typedef unsigned char	u8;
typedef signed short	s16;

I usually write usigned char or int before each variable?
Is it just for making life easier ?? ;)


Adam,

While I understand Peter's reason for making his own typedefs (shorter typing) the fact is that there is a C99 standard for integer types these days which is:

uint8_t
int8_t
uint16_t
int16_t
uint32_t
int32_t
uint64_t
int64_t

You are far better off using something like 'uint16_t' rather than 'unsigned short' or 'unsigned int' because it's guaranteed on any processor that uint16_t will deliver a variable held in 16 bits that is unsigned. On an AVR this could be either 'unsigned short' or 'unsigned int' because they are both 16 bits but if you took the same code to an ARM then the only type to deliver 16 bits unsigned is 'unsigned short' (on the ARM 'unsigned int' is 32 bits in fact).

Bottom line is that using these types aids code portability (and makes it more self documenting - it's clearly the programmers intention that he wanted to use values from 0 to 65535 when he uses uint16_t)

Cliff

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

Thank you for such a solid replay to my questions! Now everything is clear :) :) :)

Peter in question number 4 I didn`t know what was going on in line :

TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5);   // preload for 10ms

Know I know that you used casting !
Know exacly what you do in this line know :)
That`s why I was asking about (u8) and (s16) :)
I thought that (u8) is equal 2e8 and (s16) is equal 2e16 :) :) :)

But really what for you use (s16) casting ??
Only (u8) brings some effects but
when I got rid of (s16) TCNTO is still equall 246 ??

TCNT0 = (u8)-(XTAL / 1024 * 10e-3 + 0.5);   // preload for 10ms

Does this line do the same what orginally in your code ??
In AVRstudio when I got rid of it nothing bad happend ;) as I said still the same value in TCTNO :)

Cliff, thank you for another great explanation of the subject. Now I think I will be using C99 standard :)
Maybe my rubbish code will be more readable ;) ;) ;) and compatible with other microcontrollers :) :) :)
Your posts are great tutorials :)

Thank you guys for all your help and support !

If you could just explain me this thing with casting I would be really happy :) :) :)

Take Care

Adam :)

Forgot to paste my small input in Danni`s code ;) :

#include 
#include 
#include 


// typedef unsigned char u8;
// typedef signed short	s16;

#define	XTAL		1e6		// 1MHz

//handling inputs

#define KEY_PIN		PINB
#define KEY0		0
#define KEY1		1
#define KEY2		2

//handling outputs

#define LED_DDR		DDRD
#define LED_PORT	PORTD
#define LED0		0
#define LED1		1
#define LED2		2

#define REPEAT_MASK	 ((1<<KEY1)|(1<<KEY2))	// repeat: key1, key2
#define REPEAT_START 50		// after 500ms
#define REPEAT_NEXT	 20		// every 200ms


uint8_t key_state;				// debounced and inverted key state:
								// bit = 1: key pressed
uint8_t key_press;				// key press detect

uint8_t key_rpt;				// key long press and repeat


ISR (TIMER0_OVF_vect)			// every 10ms
{
  static uint8_t ct0, ct1, rpt;
  uint8_t i;

  TCNT0 = 	(uint8_t)(int16_t)-(XTAL / 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
  //ct2 = (ct0 & ct1) ^ (ct2 & i);
  //ct3 = (ct0 & ct1 & ct2) ^ (ct3 & i);
  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;
  }
}


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;
}


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 )
{
  TCCR0 = 1<<CS02^1<<CS00;			// divide by 1024
  TIMSK = 1<<TOIE0;				// enable timer interrupt

  LED_PORT = 0xFF;
  LED_DDR = 0xFF;
  
  sei();

  for(;;)
  
  {					// main loop
			// single press

    if( get_key_press( 1<<KEY0 ))
      LED_PORT ^= 1<<LED0;

			// release after short press: task 1
			// long press: task 2

    if( get_key_short( 1<<KEY1 ))
      LED_PORT ^= 1<<LED1;

    if( get_key_long( 1<<KEY1 ))
      LED_PORT ^= 1<<LED2;

			// single press and repeat

    if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
      uint8_t i = LED_PORT;

      i = (i & 0x07) | ((i << 1) & 0xF0);
      if( i < 0xF0 )
        i |= 0x08;
      LED_PORT = i;      
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ifryd wrote:

Know I know that you used casting !
Know exacly what you do in this line know :)
That`s why I was asking about (u8) and (s16) :)
I thought that (u8) is equal 2e8 and (s16) is equal 2e16 :) :) :)

But really what for you use (s16) casting ??
Only (u8) brings some effects but
when I got rid of (s16) TCNTO is still equall 246 ??

No, it has an effect.

Converting a negative float value to unsigned was undefined by the C-standard.

Only converting negative float to signed and signed to unsigned was defined.

So for clean C syntax you need the two stage conversion.

Peter

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

Thank You once again :)

It was really hard to understand your debouncing code but I think that now when I understand it I will be able to implement it to my project without feeling guilty that I don`t understand something.
There Is another interesting aspect that I `ve learned from your project lots of things :) :) :)

Once again thank you all guys for your help and support :)

Adam

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

Hi Ifryd,

I am newbe in AVR and I did adapt your algorithm to my keyboard (a bit different approach one because I am using databus through a Latch) and it is working ok.
However, I would like to ask you if you please could give me an explanation regarding the operationality of your code "if ( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 ))".
I didn't understand how it works. I mean, how the repeat mode works?
Thanks

Teach is learn twice. So, what do you think regarding learn again?