C_TAST.C
/************************************************************************/
/* */
/* Debouncing 8 Keys */
/* Sampling 4 Times */
/* With Repeat Function */
/* */
/* Author: Peter Dannegger */
/* danni@specs.de */
/* */
/************************************************************************/
#include <io.h>
#include <interrupt.h>
#include <signal.h>
typedef unsigned char u8;
typedef signed short s16;
#define XTAL 1e6 // 1MHz
#define KEY_PIN PINB
#define KEY0 0
#define KEY1 1
#define KEY2 2
#define LED_DDR DDRA
#define LED_PORT PORTA
#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
u8 key_state; // debounced and inverted key state:
// bit = 1: key pressed
u8 key_press; // key press detect
u8 key_rpt; // key long press and repeat
SIGNAL (SIG_OVERFLOW0) // every 10ms
{
static u8 ct0, ct1, rpt;
u8 i;
TCNT0 = (u8)(s16)-(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
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;
}
}
u8 get_key_press( u8 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;
}
u8 get_key_rpt( u8 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;
}
u8 get_key_short( u8 key_mask )
{
cli(); // read key state and key press atomic !
return get_key_press( ~key_state & key_mask );
}
u8 get_key_long( u8 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 )){
u8 i = LED_PORT;
i = (i & 0x07) | ((i << 1) & 0xF0);
if( i < 0xF0 )
i |= 0x08;
LED_PORT = i;
}
}
}