/*--------------------------------------------------------------------------
10/13/2010: Button debounce code by Mike Szczys
 
based on "danni debounce" code by Peter Dannegger:
 
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=189356#189356
 
This code detects and debounces button presses. It is tailored for use with
AVR microcontrollers but I've adapted it for other architectures easily and
successfully. It can be modified to use all eight bits on the same port
for up to eight buttons.
 
The interrupt service routine (ISR) at the bottom uses binary counter
variables (ct0 and ct1) to check the buttons once every 10ms until 40ms has
passed. If the button registeres the first and last times it reads it as
a keypress. There is no functionality in this code for detecting a held
button.
--------------------------------------------------------------------------*/
 
// F_CPU used by debounce to calculate 10ms interrupts
#define F_CPU 1200000
 
#include <avr/io.h>
#include <avr/interrupt.h>
 
//define pins used by buttons
#define KEY_DDR     DDRB
#define KEY_PORT    PORTB
#define KEY_PIN     PINB
#define KEY0        1   //Button on PB1
#define KEY1        2   //Button on PB2
 
//Debounce variables
unsigned char debounce_cnt = 0;
volatile unsigned char key_press;
unsigned char key_state;
 
/*--------------------------------------------------------------------------
  Prototypes
--------------------------------------------------------------------------*/
unsigned char get_key_press( unsigned char key_mask );
void init_timers(void);
void init_io(void);
 
/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Used to read debounced button presses
  PARAMS: A keymask corresponding to the pin for the button you with to poll
  RETURNS: A keymask where any high bits represent a button press
--------------------------------------------------------------------------*/
unsigned char get_key_press( unsigned char key_mask )
{
  cli();            // read and clear atomic !
  key_mask &= key_press;    // read key(s)
  key_press ^= key_mask;    // clear key(s)
  sei();            // enable interrupts
  return key_mask;
}
 
/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Sets and starts a system timer
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void init_timers(void)
{
  cli();            // read and clear atomic !
  //Timer0 for buttons
  TCCR0B |= 1<<CS02 | 1<<CS00;  //Divide by 1024
  TIMSK0 |= 1<<TOIE0;     //enable timer overflow interrupt
  sei();            // enable interrupts
}
 
/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Initialize input and output registers
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void init_io(void)
{
  //Setup Buttons
  KEY_DDR &= ~((1<<KEY0) | (1<<KEY1));  //Set pins as input
  KEY_PORT |= (1<<KEY0) | (1<<KEY1);    //enable pull-up resistors
}
 
/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Main
--------------------------------------------------------------------------*/
int main(void)
{
  init_timers();    //start the timer
  init_io();        //setup the buttons
 
  for (;;) //loop forever
  {
    if( get_key_press( 1<<KEY0 ))
    {
      //KEY0 press detected. Do something here
    }
 
    if (get_key_press( 1<<KEY1 ))
    {
      //KEY1 press detected. Do something here
    }
  }
}
 
//--------------------------------------------------------------------------
ISR(TIM0_OVF_vect)           // interrupt every 10ms
{
  static unsigned char ct0, ct1;
  unsigned char i;
 
  //TCNT0 is where TIMER0 starts counting. This calculates a value based on
  //the system clock speed that will cause the timer to reach an overflow
  //after exactly 10ms
  TCNT0 = (unsigned char)(signed short)-(((F_CPU / 1024) * .01) + 0.5);   // preload for 10ms interrupts
 
  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
}