/************************************************************************/
/*                                                                      */
/*                      Debouncing 8 Keys				*/
/*			Sampling 4 Times				*/
/*			With Repeat Function				*/
/*                                                                      */
/*              Author: Peter Dannegger                                 */
/*                      danni@specs.de                                  */
/*                                                                      */
/************************************************************************/

#include <io.h>
#include <interrupt.h>
#include <signal.h>

#define KEY_INPUT	PIND
#define LED_OUTPUT	PORTB


#define REPEAT_MASK	(1<<PD1^1<<PD2)	// repeat: key 1, 2
#define REPEAT_START	125		// after 500ms
#define REPEAT_NEXT	25		// every 100ms


char key_state;				// debounced and inverted key state:
					// bit = 1: key pressed
char key_press;				// key press detect

char key_rpt;				// key long press and repeat


SIGNAL (SIG_OVERFLOW0)			// every 4ms at 16MHz
{
  static char ct0, ct1, rpt;
  char i;

  i = key_state ^ ~KEY_INPUT;		// 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;
  }
}


char get_key_press( char key_mask )
{
  cli();
  key_mask &= key_press;                        // read key(s)
  key_press ^= key_mask;                        // clear key(s)
  sei();
  return key_mask;
}


char get_key_rpt( char key_mask )
{
  cli();
  key_mask &= key_rpt;                        	// read key(s)
  key_rpt ^= key_mask;                        	// clear key(s)
  sei();
  return key_mask;
}


int main( void )
{
  TCCR0 = 1<<CS02;				// divide by 256 * 256
  TIMSK = 1<<TOIE0;				// enable timer interrupt

  DDRB = 0xFF;
  sei();

  for(;;){					// main loop

		// single press:

    if( get_key_press( 1<<PD0 ))		// Key 0:
      LED_OUTPUT ^= 1<<PB0;			// toggle LED 0


		// single long press:

    if( get_key_rpt( 1<<PD1 )			// long press key 1
      && get_key_press( 1<<PD1 ))		// after short press:
      LED_OUTPUT ^= 1<<PB1;			// toggle LED 1


		// repeat on long press:

    if( get_key_press( 1<<PD2 )			// Key 2 or
      || get_key_rpt( 1<<PD2 ))			// long press Key 2:
      LED_OUTPUT++;				// LEDs count up
  }
}
