-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathinput.c
182 lines (151 loc) · 4.66 KB
/
input.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/*
This program is meant for use with an Atmel ATTiny2313, installed in a
Hall Research KP2B keypad. See hallResearchKP2B-reverseEngineering.txt
for details on the hardware.
This program is an example of how to read input from the switches.
Copyright 2012 Andrew Sampson. Released under the GPL v2. See COPYING.
*/
#include <stdint.h>
#include <avr/io.h>
#include <util/delay.h>
#define LED_A_WRITE_LATCH PD2
#define LED_B_WRITE_LATCH PD3
#define SW_A_READ_OUTPUTENABLE PD4
#define SW_B_READ_OUTPUTENABLE PD5
#define VALID_SWITCHES_MASK 0b1111111111
/*
Uses port B to talk to the LED latches.
Upon exiting, leaves port B in high-impedence state.
*/
void writeLEDs( uint16_t values )
{
// The LEDs are connected between U3/U5 and VCC. Thus, they illuminate
// when the outputs on U3/U5 go *low*. The LED values are inverted here,
// so that the rest of the program can treat 1="LED on" and 0="LED off".
uint8_t highByte = ~(values >> 8);
uint8_t lowByte = ~(values & 0xff);
// set port B pins as output
DDRB = 0b11111111;
PORTB = lowByte;
PORTD |= (1 << LED_A_WRITE_LATCH);
PORTD &= ~(1 << LED_A_WRITE_LATCH);
PORTB = highByte;
PORTD |= (1 << LED_B_WRITE_LATCH);
PORTD &= ~(1 << LED_B_WRITE_LATCH);
PORTB = 0;
// set port B pins as input
DDRB = 0;
PORTB = 0; // disable internal pull-ups
}
/*
Uses port B to talk to the switch latches.
Upon exiting, leaves port B in high-impedence state.
*/
uint16_t readSwitches( void )
{
uint16_t result = 0;
// set port B pins as input
DDRB = 0;
PORTB = 0; // disable internal pull-ups
// keep in mind that the latch OutputEnable is active low
// enable bus output for the first bank of switches
PORTD &= ~(1 << SW_A_READ_OUTPUTENABLE);
// I've found that a delay is necessary in order for the switch values
// to appear on the bus. This delay could possibly be shortened to
// just a few nops.
_delay_ms( 1 );
// grab the values from port B
result |= PINB;
// disable bus output for the switches
PORTD |= (1 << SW_A_READ_OUTPUTENABLE);
// enable bus output for the second bank of switches
PORTD &= ~(1 << SW_B_READ_OUTPUTENABLE);
// I've found that a delay is necessary in order for the switch values
// to appear on the bus. This delay could possibly be shortened to
// just a few nops.
_delay_ms( 1 );
// grab the values from port B
result |= (PINB << 8);
// disable bus output for the switches
PORTD |= (1 << SW_B_READ_OUTPUTENABLE);
// the switches are low when pressed; the values are inverted here
// so that the rest of the program can treat 1=pressed.
return ~result;
}
int main(void)
{
// configure port B
DDRB = 0;
PORTB = 0; // disable internal pull-ups
// configure port D
DDRD =
(1 << LED_A_WRITE_LATCH) |
(1 << LED_B_WRITE_LATCH) |
(1 << SW_A_READ_OUTPUTENABLE) |
(1 << SW_B_READ_OUTPUTENABLE);
PORTD = 0;
// the latch OutputEnable is active low
PORTD |= (1 << SW_A_READ_OUTPUTENABLE);
PORTD |= (1 << SW_B_READ_OUTPUTENABLE);
/*
// BTW, as a curious side-effect of how the bus is wired, you can give
// control of the bus to the switch latch. If you do this, and
// latch-enable one of the LED latches, the switch latch will write
// directly to the LED latch. Pressing the switches will light up the LEDs
// with no intervention required on the part of the microcontroller.
// Here's how to do that:
PORTD |= (1 << LED_A_WRITE_LATCH);
PORTD &= ~(1 << SW_A_READ_OUTPUTENABLE);
while( 1 ) {}
*/
// led chaser sequence
uint8_t i;
for( i = 0 ; i < 2; i++ )
{
uint16_t leds = 1;
// stop after all 10 leds have been illuminated
while( leds < (1<<11) )
{
writeLEDs( leds );
_delay_ms( 100 );
leds = leds << 1;
}
}
// blink all leds 2 times
writeLEDs( 0x0000 );
_delay_ms( 100 );
writeLEDs( 0xffff );
_delay_ms( 100 );
writeLEDs( 0x0000 );
_delay_ms( 100 );
writeLEDs( 0xffff );
_delay_ms( 100 );
writeLEDs( 0x0000 );
uint16_t switches = 0;
// read switches and update leds (momentary style),
// until user presses buttons 1 and 5
while( (switches & VALID_SWITCHES_MASK) != 0b0000010001 )
{
switches = readSwitches();
writeLEDs( switches );
}
uint16_t switchDebounce;
uint16_t leds = 0;
// read switches and toggle leds
while( 1 )
{
// Simple debounce: read the switches, pause, and read them again.
// Only count the buttons that were pressed at both samplings as
// being pressed.
switchDebounce = readSwitches();
_delay_ms( 10 );
switchDebounce &= readSwitches();
// use xor to toggle
// "(switches ^ switchDebounce) & switchDebounce" =
// "the switches that changed and are now active"
leds ^= (switches ^ switchDebounce) & switchDebounce;
writeLEDs( leds );
switches = switchDebounce;
}
return 0;
}