diff options
Diffstat (limited to 'keyboards/ergodox_ez/matrix.c')
-rw-r--r-- | keyboards/ergodox_ez/matrix.c | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/keyboards/ergodox_ez/matrix.c b/keyboards/ergodox_ez/matrix.c new file mode 100644 index 000000000..b743cf0d6 --- /dev/null +++ b/keyboards/ergodox_ez/matrix.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /* | ||
2 | |||
3 | Note for ErgoDox EZ customizers: Here be dragons! | ||
4 | This is not a file you want to be messing with. | ||
5 | All of the interesting stuff for you is under keymaps/ :) | ||
6 | Love, Erez | ||
7 | |||
8 | Copyright 2013 Oleg Kostyuk <cub.uanic@gmail.com> | ||
9 | |||
10 | This program is free software: you can redistribute it and/or modify | ||
11 | it under the terms of the GNU General Public License as published by | ||
12 | the Free Software Foundation, either version 2 of the License, or | ||
13 | (at your option) any later version. | ||
14 | |||
15 | This program is distributed in the hope that it will be useful, | ||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | GNU General Public License for more details. | ||
19 | |||
20 | You should have received a copy of the GNU General Public License | ||
21 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
22 | */ | ||
23 | |||
24 | /* | ||
25 | * scan matrix | ||
26 | */ | ||
27 | #include <stdint.h> | ||
28 | #include <stdbool.h> | ||
29 | #include <avr/io.h> | ||
30 | #include "wait.h" | ||
31 | #include "action_layer.h" | ||
32 | #include "print.h" | ||
33 | #include "debug.h" | ||
34 | #include "util.h" | ||
35 | #include "matrix.h" | ||
36 | #include QMK_KEYBOARD_H | ||
37 | #include "i2cmaster.h" | ||
38 | #ifdef DEBUG_MATRIX_SCAN_RATE | ||
39 | #include "timer.h" | ||
40 | #endif | ||
41 | |||
42 | /* | ||
43 | * This constant define not debouncing time in msecs, but amount of matrix | ||
44 | * scan loops which should be made to get stable debounced results. | ||
45 | * | ||
46 | * On Ergodox matrix scan rate is relatively low, because of slow I2C. | ||
47 | * Now it's only 317 scans/second, or about 3.15 msec/scan. | ||
48 | * According to Cherry specs, debouncing time is 5 msec. | ||
49 | * | ||
50 | * And so, there is no sense to have DEBOUNCE higher than 2. | ||
51 | */ | ||
52 | |||
53 | #ifndef DEBOUNCE | ||
54 | # define DEBOUNCE 5 | ||
55 | #endif | ||
56 | |||
57 | /* matrix state(1:on, 0:off) */ | ||
58 | static matrix_row_t matrix[MATRIX_ROWS]; | ||
59 | |||
60 | // Debouncing: store for each key the number of scans until it's eligible to | ||
61 | // change. When scanning the matrix, ignore any changes in keys that have | ||
62 | // already changed in the last DEBOUNCE scans. | ||
63 | static uint8_t debounce_matrix[MATRIX_ROWS * MATRIX_COLS]; | ||
64 | |||
65 | static matrix_row_t read_cols(uint8_t row); | ||
66 | static void init_cols(void); | ||
67 | static void unselect_rows(void); | ||
68 | static void select_row(uint8_t row); | ||
69 | |||
70 | static uint8_t mcp23018_reset_loop; | ||
71 | |||
72 | #ifdef DEBUG_MATRIX_SCAN_RATE | ||
73 | uint32_t matrix_timer; | ||
74 | uint32_t matrix_scan_count; | ||
75 | #endif | ||
76 | |||
77 | |||
78 | __attribute__ ((weak)) | ||
79 | void matrix_init_user(void) {} | ||
80 | |||
81 | __attribute__ ((weak)) | ||
82 | void matrix_scan_user(void) {} | ||
83 | |||
84 | __attribute__ ((weak)) | ||
85 | void matrix_init_kb(void) { | ||
86 | matrix_init_user(); | ||
87 | } | ||
88 | |||
89 | __attribute__ ((weak)) | ||
90 | void matrix_scan_kb(void) { | ||
91 | matrix_scan_user(); | ||
92 | } | ||
93 | |||
94 | inline | ||
95 | uint8_t matrix_rows(void) | ||
96 | { | ||
97 | return MATRIX_ROWS; | ||
98 | } | ||
99 | |||
100 | inline | ||
101 | uint8_t matrix_cols(void) | ||
102 | { | ||
103 | return MATRIX_COLS; | ||
104 | } | ||
105 | |||
106 | void matrix_init(void) | ||
107 | { | ||
108 | // initialize row and col | ||
109 | |||
110 | mcp23018_status = init_mcp23018(); | ||
111 | |||
112 | |||
113 | unselect_rows(); | ||
114 | init_cols(); | ||
115 | |||
116 | // initialize matrix state: all keys off | ||
117 | for (uint8_t i=0; i < MATRIX_ROWS; i++) { | ||
118 | matrix[i] = 0; | ||
119 | for (uint8_t j=0; j < MATRIX_COLS; ++j) { | ||
120 | debounce_matrix[i * MATRIX_COLS + j] = 0; | ||
121 | } | ||
122 | } | ||
123 | |||
124 | #ifdef DEBUG_MATRIX_SCAN_RATE | ||
125 | matrix_timer = timer_read32(); | ||
126 | matrix_scan_count = 0; | ||
127 | #endif | ||
128 | |||
129 | matrix_init_quantum(); | ||
130 | |||
131 | } | ||
132 | |||
133 | void matrix_power_up(void) { | ||
134 | mcp23018_status = init_mcp23018(); | ||
135 | |||
136 | unselect_rows(); | ||
137 | init_cols(); | ||
138 | |||
139 | // initialize matrix state: all keys off | ||
140 | for (uint8_t i=0; i < MATRIX_ROWS; i++) { | ||
141 | matrix[i] = 0; | ||
142 | } | ||
143 | |||
144 | #ifdef DEBUG_MATRIX_SCAN_RATE | ||
145 | matrix_timer = timer_read32(); | ||
146 | matrix_scan_count = 0; | ||
147 | #endif | ||
148 | } | ||
149 | |||
150 | // Returns a matrix_row_t whose bits are set if the corresponding key should be | ||
151 | // eligible to change in this scan. | ||
152 | matrix_row_t debounce_mask(uint8_t row) { | ||
153 | matrix_row_t result = 0; | ||
154 | for (uint8_t j=0; j < MATRIX_COLS; ++j) { | ||
155 | if (debounce_matrix[row * MATRIX_COLS + j]) { | ||
156 | --debounce_matrix[row * MATRIX_COLS + j]; | ||
157 | } else { | ||
158 | result |= (1 << j); | ||
159 | } | ||
160 | } | ||
161 | return result; | ||
162 | } | ||
163 | |||
164 | // Report changed keys in the given row. Resets the debounce countdowns | ||
165 | // corresponding to each set bit in 'change' to DEBOUNCE. | ||
166 | void debounce_report(matrix_row_t change, uint8_t row) { | ||
167 | for (uint8_t i = 0; i < MATRIX_COLS; ++i) { | ||
168 | if (change & (1 << i)) { | ||
169 | debounce_matrix[row * MATRIX_COLS + i] = DEBOUNCE; | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | |||
174 | uint8_t matrix_scan(void) | ||
175 | { | ||
176 | if (mcp23018_status) { // if there was an error | ||
177 | if (++mcp23018_reset_loop == 0) { | ||
178 | // since mcp23018_reset_loop is 8 bit - we'll try to reset once in 255 matrix scans | ||
179 | // this will be approx bit more frequent than once per second | ||
180 | print("trying to reset mcp23018\n"); | ||
181 | mcp23018_status = init_mcp23018(); | ||
182 | if (mcp23018_status) { | ||
183 | print("left side not responding\n"); | ||
184 | } else { | ||
185 | print("left side attached\n"); | ||
186 | ergodox_blink_all_leds(); | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | |||
191 | #ifdef DEBUG_MATRIX_SCAN_RATE | ||
192 | matrix_scan_count++; | ||
193 | |||
194 | uint32_t timer_now = timer_read32(); | ||
195 | if (TIMER_DIFF_32(timer_now, matrix_timer)>1000) { | ||
196 | print("matrix scan frequency: "); | ||
197 | pdec(matrix_scan_count); | ||
198 | print("\n"); | ||
199 | |||
200 | matrix_timer = timer_now; | ||
201 | matrix_scan_count = 0; | ||
202 | } | ||
203 | #endif | ||
204 | |||
205 | for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | ||
206 | select_row(i); | ||
207 | wait_us(30); // without this wait read unstable value. | ||
208 | matrix_row_t mask = debounce_mask(i); | ||
209 | matrix_row_t cols = (read_cols(i) & mask) | (matrix[i] & ~mask); | ||
210 | debounce_report(cols ^ matrix[i], i); | ||
211 | matrix[i] = cols; | ||
212 | |||
213 | unselect_rows(); | ||
214 | } | ||
215 | |||
216 | matrix_scan_quantum(); | ||
217 | |||
218 | return 1; | ||
219 | } | ||
220 | |||
221 | bool matrix_is_modified(void) // deprecated and evidently not called. | ||
222 | { | ||
223 | return true; | ||
224 | } | ||
225 | |||
226 | inline | ||
227 | bool matrix_is_on(uint8_t row, uint8_t col) | ||
228 | { | ||
229 | return (matrix[row] & ((matrix_row_t)1<<col)); | ||
230 | } | ||
231 | |||
232 | inline | ||
233 | matrix_row_t matrix_get_row(uint8_t row) | ||
234 | { | ||
235 | return matrix[row]; | ||
236 | } | ||
237 | |||
238 | void matrix_print(void) | ||
239 | { | ||
240 | print("\nr/c 0123456789ABCDEF\n"); | ||
241 | for (uint8_t row = 0; row < MATRIX_ROWS; row++) { | ||
242 | phex(row); print(": "); | ||
243 | pbin_reverse16(matrix_get_row(row)); | ||
244 | print("\n"); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | uint8_t matrix_key_count(void) | ||
249 | { | ||
250 | uint8_t count = 0; | ||
251 | for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | ||
252 | count += bitpop16(matrix[i]); | ||
253 | } | ||
254 | return count; | ||
255 | } | ||
256 | |||
257 | /* Column pin configuration | ||
258 | * | ||
259 | * Teensy | ||
260 | * col: 0 1 2 3 4 5 | ||
261 | * pin: F0 F1 F4 F5 F6 F7 | ||
262 | * | ||
263 | * MCP23018 | ||
264 | * col: 0 1 2 3 4 5 | ||
265 | * pin: B5 B4 B3 B2 B1 B0 | ||
266 | */ | ||
267 | static void init_cols(void) | ||
268 | { | ||
269 | // init on mcp23018 | ||
270 | // not needed, already done as part of init_mcp23018() | ||
271 | |||
272 | // init on teensy | ||
273 | // Input with pull-up(DDR:0, PORT:1) | ||
274 | DDRF &= ~(1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<1 | 1<<0); | ||
275 | PORTF |= (1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<1 | 1<<0); | ||
276 | } | ||
277 | |||
278 | static matrix_row_t read_cols(uint8_t row) | ||
279 | { | ||
280 | if (row < 7) { | ||
281 | if (mcp23018_status) { // if there was an error | ||
282 | return 0; | ||
283 | } else { | ||
284 | uint8_t data = 0; | ||
285 | mcp23018_status = i2c_start(I2C_ADDR_WRITE); if (mcp23018_status) goto out; | ||
286 | mcp23018_status = i2c_write(GPIOB); if (mcp23018_status) goto out; | ||
287 | mcp23018_status = i2c_start(I2C_ADDR_READ); if (mcp23018_status) goto out; | ||
288 | data = i2c_readNak(); | ||
289 | data = ~data; | ||
290 | out: | ||
291 | i2c_stop(); | ||
292 | return data; | ||
293 | } | ||
294 | } else { | ||
295 | // read from teensy | ||
296 | return | ||
297 | (PINF&(1<<0) ? 0 : (1<<0)) | | ||
298 | (PINF&(1<<1) ? 0 : (1<<1)) | | ||
299 | (PINF&(1<<4) ? 0 : (1<<2)) | | ||
300 | (PINF&(1<<5) ? 0 : (1<<3)) | | ||
301 | (PINF&(1<<6) ? 0 : (1<<4)) | | ||
302 | (PINF&(1<<7) ? 0 : (1<<5)) ; | ||
303 | } | ||
304 | } | ||
305 | |||
306 | /* Row pin configuration | ||
307 | * | ||
308 | * Teensy | ||
309 | * row: 7 8 9 10 11 12 13 | ||
310 | * pin: B0 B1 B2 B3 D2 D3 C6 | ||
311 | * | ||
312 | * MCP23018 | ||
313 | * row: 0 1 2 3 4 5 6 | ||
314 | * pin: A0 A1 A2 A3 A4 A5 A6 | ||
315 | */ | ||
316 | static void unselect_rows(void) | ||
317 | { | ||
318 | // unselect on mcp23018 | ||
319 | if (mcp23018_status) { // if there was an error | ||
320 | // do nothing | ||
321 | } else { | ||
322 | // set all rows hi-Z : 1 | ||
323 | mcp23018_status = i2c_start(I2C_ADDR_WRITE); if (mcp23018_status) goto out; | ||
324 | mcp23018_status = i2c_write(GPIOA); if (mcp23018_status) goto out; | ||
325 | mcp23018_status = i2c_write( 0xFF | ||
326 | & ~(0<<7) | ||
327 | ); if (mcp23018_status) goto out; | ||
328 | out: | ||
329 | i2c_stop(); | ||
330 | } | ||
331 | |||
332 | // unselect on teensy | ||
333 | // Hi-Z(DDR:0, PORT:0) to unselect | ||
334 | DDRB &= ~(1<<0 | 1<<1 | 1<<2 | 1<<3); | ||
335 | PORTB &= ~(1<<0 | 1<<1 | 1<<2 | 1<<3); | ||
336 | DDRD &= ~(1<<2 | 1<<3); | ||
337 | PORTD &= ~(1<<2 | 1<<3); | ||
338 | DDRC &= ~(1<<6); | ||
339 | PORTC &= ~(1<<6); | ||
340 | } | ||
341 | |||
342 | static void select_row(uint8_t row) | ||
343 | { | ||
344 | if (row < 7) { | ||
345 | // select on mcp23018 | ||
346 | if (mcp23018_status) { // if there was an error | ||
347 | // do nothing | ||
348 | } else { | ||
349 | // set active row low : 0 | ||
350 | // set other rows hi-Z : 1 | ||
351 | mcp23018_status = i2c_start(I2C_ADDR_WRITE); if (mcp23018_status) goto out; | ||
352 | mcp23018_status = i2c_write(GPIOA); if (mcp23018_status) goto out; | ||
353 | mcp23018_status = i2c_write( 0xFF & ~(1<<row) | ||
354 | & ~(0<<7) | ||
355 | ); if (mcp23018_status) goto out; | ||
356 | out: | ||
357 | i2c_stop(); | ||
358 | } | ||
359 | } else { | ||
360 | // select on teensy | ||
361 | // Output low(DDR:1, PORT:0) to select | ||
362 | switch (row) { | ||
363 | case 7: | ||
364 | DDRB |= (1<<0); | ||
365 | PORTB &= ~(1<<0); | ||
366 | break; | ||
367 | case 8: | ||
368 | DDRB |= (1<<1); | ||
369 | PORTB &= ~(1<<1); | ||
370 | break; | ||
371 | case 9: | ||
372 | DDRB |= (1<<2); | ||
373 | PORTB &= ~(1<<2); | ||
374 | break; | ||
375 | case 10: | ||
376 | DDRB |= (1<<3); | ||
377 | PORTB &= ~(1<<3); | ||
378 | break; | ||
379 | case 11: | ||
380 | DDRD |= (1<<2); | ||
381 | PORTD &= ~(1<<3); | ||
382 | break; | ||
383 | case 12: | ||
384 | DDRD |= (1<<3); | ||
385 | PORTD &= ~(1<<3); | ||
386 | break; | ||
387 | case 13: | ||
388 | DDRC |= (1<<6); | ||
389 | PORTC &= ~(1<<6); | ||
390 | break; | ||
391 | } | ||
392 | } | ||
393 | } | ||
394 | |||