1  | /************************************************************************/
  | 
2  | /*                                                                      */
  | 
3  | /*    Debouncing 8 Keys        */
  | 
4  | /*        Sampling 4 Times        */
  | 
5  | /*    With Repeat Function        */
  | 
6  | /*                                                                      */
  | 
7  | /*     Author: Peter Dannegger                                 */
  | 
8  | /*          danni@specs.de                                  */
  | 
9  | /*                                                                      */
  | 
10  | /************************************************************************/
  | 
11  | // Target: ATMega48
  | 
12  | 
  | 
13  | #include <io.h>
  | 
14  | #include <interrupt.h>
  | 
15  | 
  | 
16  | typedef unsigned char  u8;
  | 
17  | typedef signed short  s16;
  | 
18  | 
  | 
19  | #define  XTAL    8000000L        // 8MHz
  | 
20  | 
  | 
21  | #define KEY_PIN    PINB
  | 
22  | #define KEY0    0
  | 
23  | #define KEY1    1
  | 
24  | #define KEY2    2
  | 
25  | #define  KEY3    3
  | 
26  | 
  | 
27  | #define LED_DDR    DDRC
  | 
28  | #define LED_PORT  PORTC
  | 
29  | #define LED0    0
  | 
30  | #define LED1    1
  | 
31  | #define LED2    2
  | 
32  | 
  | 
33  | #define REPEAT_MASK  (1<<KEY3^1<<KEY2^1<<KEY1^1<<KEY0)
  | 
34  | #define REPEAT_START  10          // after 100ms
  | 
35  | #define REPEAT_NEXT  20          // every 200ms
  | 
36  | 
  | 
37  | u8 key_state;                // debounced and inverted key state:
  | 
38  |                     // bit = 1: key pressed
  | 
39  | u8 key_press;                // key press detect
  | 
40  | 
  | 
41  | u8 key_rpt;                // key long press and repeat
  | 
42  | 
  | 
43  | 
  | 
44  | ISR( TIMER0_COMPA_vect )       // every 10ms
  | 
45  | {
 | 
46  |   static u8 ct0, ct1, rpt;
  | 
47  |   u8 i;
  | 
48  | 
  | 
49  |   i = key_state ^ ~KEY_PIN;        // key changed ?
  | 
50  |   ct0 = ~( ct0 & i );            // reset or count ct0
  | 
51  |   ct1 = ct0 ^ (ct1 & i);        // reset or count ct1
  | 
52  |   i &= ct0 & ct1;            // count until roll over ?
  | 
53  |   key_state ^= i;            // then toggle debounced state
  | 
54  |   key_press |= key_state & i;      // 0->1: key press detect
  | 
55  | 
  | 
56  |   if( (key_state & REPEAT_MASK) == 0 )  // check repeat function
  | 
57  |      rpt = REPEAT_START;        // start delay
  | 
58  |   if( --rpt == 0 ){
 | 
59  |     rpt = REPEAT_NEXT;          // repeat delay
  | 
60  |     key_rpt |= key_state & REPEAT_MASK;
  | 
61  |    }
  | 
62  | }
  | 
63  | 
  | 
64  | 
  | 
65  | 
  | 
66  | u8 get_key_press( u8 key_mask )
  | 
67  | {
 | 
68  |   cli();                  // read and clear atomic !
  | 
69  |   key_mask &= key_press;                            // read key(s)
  | 
70  |   key_press ^= key_mask;                            // clear key(s)
  | 
71  |   sei();
  | 
72  |   return key_mask;
  | 
73  | }
  | 
74  | 
  | 
75  | u8 get_key_rpt( u8 key_mask )
  | 
76  | {
 | 
77  |   cli();                  // read and clear atomic !
  | 
78  |   key_mask &= key_rpt;                              // read key(s)
  | 
79  |   key_rpt ^= key_mask;                              // clear key(s)
  | 
80  |   sei();
  | 
81  |   return key_mask;
  | 
82  | }
  | 
83  | 
  | 
84  | u8 get_key_short( u8 key_mask )
  | 
85  | {
 | 
86  |   cli();                // read key state and key press atomic !
  | 
87  |   return get_key_press( ~key_state & key_mask );
  | 
88  | }
  | 
89  | 
  | 
90  | u8 get_key_long( u8 key_mask )
  | 
91  | {
 | 
92  |   return get_key_press( get_key_rpt( key_mask ));
  | 
93  | }
  | 
94  | 
  | 
95  | int main( void )
  | 
96  | {
 | 
97  |   TCCR0A = 1<<WGM01;              // Mode 2: CTC
  | 
98  |   TCCR0B = 1<<CS02^1<<CS00;            // divide by 1024
  | 
99  |   OCR0A = (s16)(XTAL / 1024.0 * 10e-3 - 0.5);    // preload for 10ms
  | 
100  |   TIMSK0 = 1<<OCIE0A;              // enable timer interrupt
  | 
101  | 
  | 
102  |   LED_PORT = 0xFF;
  | 
103  |   LED_DDR = 0xFF;
  | 
104  |   sei();
  | 
105  | 
  | 
106  |   for(;;){                  // main loop
 | 
107  | 
  | 
108  |     switch( get_key_long( 1<<KEY1^1<<KEY0 )){
 | 
109  | 
  | 
110  |       case 1<<KEY0:          LED_PORT ^= 1<<LED0; break;
  | 
111  | 
  | 
112  |       case 1<<KEY1:          LED_PORT ^= 1<<LED1; break;
  | 
113  | 
  | 
114  |       case 1<<KEY1^1<<KEY0:  LED_PORT ^= 1<<LED2; break;
  | 
115  |     }
  | 
116  | 
  | 
117  |     switch( get_key_rpt( 1<<KEY3^1<<KEY2 )){
 | 
118  | 
  | 
119  |       case 1<<KEY2:          LED_PORT ^= 1<<LED0; break;
  | 
120  | 
  | 
121  |       case 1<<KEY3:          LED_PORT ^= 1<<LED1; break;
  | 
122  | 
  | 
123  |       case 1<<KEY3^1<<KEY2:  LED_PORT ^= 1<<LED2; break;
  | 
124  |     }
  | 
125  |   }
  | 
126  | }
  |