-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathKeyboard.c
212 lines (169 loc) · 4.08 KB
/
Keyboard.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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// Copyright 2013 David Monro [email protected]
// The main file.
// Actually I shoudl abstract out the AVR-specific bits oneday...
#include "kbdcont.h"
#include <stdarg.h>
#include <inttypes.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/io.h>
#include <util/atomic.h>
// Globals
volatile unsigned long millis = 0;
volatile uint8_t do_suspend = 0;
volatile uint8_t pending_key_wake = 0;
volatile uint8_t was_usb_wake = 0;
char realoutstr[70];
char *outstr;
unsigned long next_idle_retrans = 500;
unsigned long suppress_expire = 0;
uint8_t do_suppress = 0;
// Locals
volatile static uint8_t was_sleeping = 0;
// Interrupt service routines
// Fires once per millisecond
ISR(TIMER0_COMPA_vect)
{
millis++;
}
// Fires if any keyboard row changes
ISR(PCINT0_vect)
{
SerialDebug(3, "PINCHG\r\n");
}
// Catchall
ISR(BADISR_vect)
{
SerialDebug(1, "unk int\r\n");
}
#ifdef SERIALDEBUG
// Debugging
void SerialDebug2(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vsprintf(outstr, fmt, args);
uartPuts(outstr);
va_end(args);
}
#endif
void goToSleep(void)
{
SerialDebug(3, "sleeping\r\n");
// Only do it once...
do_suspend = 0;
// Clear these, one will be set later
pending_key_wake = 0;
was_usb_wake = 0;
// prepare the hardware
kbdActivateAllColumns();
kbdConfigureInterruptsForSleep();
// housekeeping
was_sleeping = 1;
// Actually do it
debugLedOff(3);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode();
// And now we are back
debugLedOn(3);
if (was_usb_wake) {
SerialDebug(1, "WAKE:usb\r\n");
was_usb_wake = 0;
pending_key_wake = 0;
} else {
SerialDebug(1, "WAKE:key\r\n");
// If we got woken by a row interrupt, we need to see
// if there really was a keypress
pending_key_wake = 1;
}
do_suppress = 1; // Always pause a bit after waking
suppress_expire = (unsigned long)getMillis() + 20UL;
}
volatile unsigned long int getMillis(void)
{
unsigned long int localcopy;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
localcopy = millis;
}
return localcopy;
}
void initInterrupts(void)
{
sei();
SerialDebug(2, "SREG %02x\r\n", SREG);
}
void timerInit(void)
{
// Use OCR0A for count (wg mode 2), use a 64 prescaler
TCCR0A = _BV(WGM01);
TCCR0B = _BV(CS01) | _BV(CS00);
// Set the ICR1 reg for 1ms count
OCR0A = 250;
// Set the top interrupt enable
TIMSK0 = _BV(OCIE0A);
}
int main(void)
{
unsigned long int nextscan = 0;
unsigned long int nextprint = 0;
outstr = realoutstr; // declaration issue
#if (ARCH == ARCH_AVR8)
/* Disable watchdog if enabled by bootloader/fuses */
MCUSR &= ~(1 << WDRF);
wdt_disable();
/* Disable clock division */
clock_prescale_set(clock_div_1);
#endif
timerInit(); // interrupts every ms
#ifdef SERIALDEBUG
uartInit();
#endif
SerialDebug(1, "Hello World\r\n");
debugLedOn(3);
// Get the rest of the hardware set up
initKeyboardHardware();
initKeyboardMatrix();
initScanProcessor();
// And now the interrupt layer
kbdInitInterruptSources();
initInterrupts();
// Wait 100ms for the bus to settle down
while (1) {
unsigned long now = getMillis();
if ((signed long)now - (signed long)100 >= 0) {
break;
}
}
// This will enable the USB interrupt as well
USB_Init();
// Main loop
while (1) {
unsigned long now = getMillis();
// Toggle an LED and optionally print a message every second
if ((signed long)now - (signed long)nextprint >= 0) {
SerialDebug(3,
"now %lu, idlr %d, prt %d, nxti = %lu\r\n",
now, (int)idle_rate, (int)active_protocol,
next_idle_retrans);
debugLedToggle(0);
nextprint = now + 1000;
}
// We do a scan every 5ms
if ((signed long)now - (signed long)nextscan >= 0) {
nextscan = now + 5;
doKeyboardScan();
}
// Needs to be run often
USB_Poll();
// if the usb bus goes quiet...
if (do_suspend) {
goToSleep();
}
// Idle mode leaves all the timers running, so we will sleep at most 1ms
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_mode();
}
}