]> git.donarmstrong.com Git - tmk_firmware.git/blob - keyboard/ergodox/matrix.c
Optimizing I2C
[tmk_firmware.git] / keyboard / ergodox / matrix.c
1 /*
2 Copyright 2013 Oleg Kostyuk <cub.uanic@gmail.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19  * scan matrix
20  */
21 #include <stdint.h>
22 #include <stdbool.h>
23 #include <avr/io.h>
24 #include <util/delay.h>
25 #include "action_layer.h"
26 #include "print.h"
27 #include "debug.h"
28 #include "util.h"
29 #include "matrix.h"
30 #include "ergodox.h"
31 #include "i2cmaster.h"
32 #ifdef DEBUG_MATRIX_FREQ
33 #include  "timer.h"
34 #endif
35
36 #ifndef DEBOUNCE
37 #   define DEBOUNCE     5
38 #endif
39 static uint8_t debouncing = DEBOUNCE;
40
41 /* matrix state(1:on, 0:off) */
42 static matrix_row_t matrix[MATRIX_ROWS];
43 static matrix_row_t matrix_debouncing[MATRIX_ROWS];
44
45 static matrix_row_t read_cols(uint8_t row);
46 static void init_cols(void);
47 static void unselect_rows();
48 static void select_row(uint8_t row);
49
50 static uint8_t mcp23018_status;
51
52 #ifdef DEBUG_MATRIX_FREQ
53 uint32_t matrix_timer;
54 uint32_t matrix_scan_count;
55 #endif
56
57 inline
58 uint8_t matrix_rows(void)
59 {
60     return MATRIX_ROWS;
61 }
62
63 inline
64 uint8_t matrix_cols(void)
65 {
66     return MATRIX_COLS;
67 }
68
69 void matrix_init(void)
70 {
71     // initialize row and col
72     init_ergodox();
73     mcp23018_status = init_mcp23018();
74     unselect_rows();
75     init_cols();
76
77     // initialize matrix state: all keys off
78     for (uint8_t i=0; i < MATRIX_ROWS; i++) {
79         matrix[i] = 0;
80         matrix_debouncing[i] = 0;
81     }
82
83 #ifdef DEBUG_MATRIX_FREQ
84     matrix_timer = timer_read32();
85     matrix_scan_count = 0;
86 #endif
87 }
88
89 uint8_t matrix_scan(void)
90 {
91 #ifdef DEBUG_MATRIX_FREQ
92     matrix_scan_count++;
93
94     uint32_t timer_now = timer_read32();
95     if (TIMER_DIFF_32(timer_now, matrix_timer)>1000) {
96         print("matrix scan frequency: ");
97         pdec(matrix_scan_count);
98         print("\n");
99
100         matrix_timer = timer_now;
101         matrix_scan_count = 0;
102     }
103 #endif
104
105 #ifdef KEYMAP_CUB
106     uint8_t layer = biton32(layer_state);
107
108     if (layer == 1) {
109         ergodox_left_led_1_on();
110         ergodox_left_led_2_off();
111         ergodox_left_led_3_off();
112     } else if (layer == 2) {
113         ergodox_left_led_1_off();
114         ergodox_left_led_2_on();
115         ergodox_left_led_3_off();
116     } else if (layer == 3) {
117         ergodox_left_led_1_off();
118         ergodox_left_led_2_off();
119         ergodox_left_led_3_on();
120     } else if (layer == 4) {
121         ergodox_left_led_1_on();
122         ergodox_left_led_2_off();
123         ergodox_left_led_3_on();
124     } else if (layer == 5) {
125         ergodox_left_led_1_on();
126         ergodox_left_led_2_on();
127         ergodox_left_led_3_off();
128     } else if (layer == 6) {
129         ergodox_left_led_1_off();
130         ergodox_left_led_2_on();
131         ergodox_left_led_3_on();
132     } else if (layer == 7) {
133         ergodox_left_led_1_on();
134         ergodox_left_led_2_on();
135         ergodox_left_led_3_on();
136     } else {
137         ergodox_left_led_1_off();
138         ergodox_left_led_2_off();
139         ergodox_left_led_3_off();
140     }
141
142     // not actually needed because we already calling init_mcp23018() in next line
143     mcp23018_status = ergodox_left_leds_update();
144
145 #endif
146
147     for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
148         select_row(i);
149         _delay_us(30);  // without this wait read unstable value.
150         matrix_row_t cols = read_cols(i);
151         if (matrix_debouncing[i] != cols) {
152             matrix_debouncing[i] = cols;
153             if (debouncing) {
154                 debug("bounce!: "); debug_hex(debouncing); debug("\n");
155             }
156             debouncing = DEBOUNCE;
157         }
158         unselect_rows();
159     }
160
161     if (debouncing) {
162         if (--debouncing) {
163             _delay_ms(1);
164         } else {
165             for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
166                 matrix[i] = matrix_debouncing[i];
167             }
168         }
169     }
170
171     return 1;
172 }
173
174 bool matrix_is_modified(void)
175 {
176     if (debouncing) return false;
177     return true;
178 }
179
180 inline
181 bool matrix_is_on(uint8_t row, uint8_t col)
182 {
183     return (matrix[row] & ((matrix_row_t)1<<col));
184 }
185
186 inline
187 matrix_row_t matrix_get_row(uint8_t row)
188 {
189     return matrix[row];
190 }
191
192 void matrix_print(void)
193 {
194     print("\nr/c 0123456789ABCDEF\n");
195     for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
196         phex(row); print(": ");
197         pbin_reverse16(matrix_get_row(row));
198         print("\n");
199     }
200 }
201
202 uint8_t matrix_key_count(void)
203 {
204     uint8_t count = 0;
205     for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
206         count += bitpop16(matrix[i]);
207     }
208     return count;
209 }
210
211 /* Column pin configuration
212  *
213  * Teensy
214  * col: 0   1   2   3   4   5
215  * pin: F0  F1  F4  F5  F6  F7 
216  *
217  * MCP23018
218  * col: 0   1   2   3   4   5
219  * pin: B5  B4  B3  B2  B1  B0 
220  */
221 static void  init_cols(void)
222 {
223     // init on mcp23018
224     // not needed, already done as part of init_mcp23018()
225
226     // init on teensy
227     // Input with pull-up(DDR:0, PORT:1)
228     DDRF  &= ~(1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<1 | 1<<0);
229     PORTF |=  (1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<1 | 1<<0);
230 }
231
232 static matrix_row_t read_cols(uint8_t row)
233 {
234     if (row < 7) {
235         if (mcp23018_status) { // if there was an error
236             return 0;
237         } else {
238             uint8_t data = 0;
239             mcp23018_status = i2c_start(I2C_ADDR_WRITE);    if (mcp23018_status) goto out;
240             mcp23018_status = i2c_write(GPIOB);             if (mcp23018_status) goto out;
241             mcp23018_status = i2c_start(I2C_ADDR_READ);     if (mcp23018_status) goto out;
242             data = i2c_readNak();
243             data = ~data;
244         out:
245             i2c_stop();
246             return data;
247         }
248     } else {
249         // read from teensy
250         return
251             (PINF&(1<<0) ? 0 : (1<<0)) |
252             (PINF&(1<<1) ? 0 : (1<<1)) |
253             (PINF&(1<<4) ? 0 : (1<<2)) |
254             (PINF&(1<<5) ? 0 : (1<<3)) |
255             (PINF&(1<<6) ? 0 : (1<<4)) |
256             (PINF&(1<<7) ? 0 : (1<<5)) ;
257     }
258 }
259
260 /* Row pin configuration
261  *
262  * Teensy
263  * row: 7   8   9   10  11  12  13
264  * pin: B0  B1  B2  B3  D2  D3  C6
265  *
266  * MCP23018
267  * row: 0   1   2   3   4   5   6
268  * pin: A0  A1  A2  A3  A4  A5  A6
269  */
270 static void unselect_rows(void)
271 {
272     // unselect on mcp23018
273     if (mcp23018_status) { // if there was an error
274         // do nothing
275     } else {
276         // set all rows hi-Z : 1
277         mcp23018_status = i2c_start(I2C_ADDR_WRITE);    if (mcp23018_status) goto out;
278         mcp23018_status = i2c_write(GPIOA);             if (mcp23018_status) goto out;
279         mcp23018_status = i2c_write( 0xFF
280                               & ~(ergodox_left_led_3<<LEFT_LED_3_SHIFT)
281                           );                            if (mcp23018_status) goto out;
282     out:
283         i2c_stop();
284     }
285
286     // unselect on teensy
287     // Hi-Z(DDR:0, PORT:0) to unselect
288     DDRB  &= ~(1<<0 | 1<<1 | 1<<2 | 1<<3);
289     PORTB &= ~(1<<0 | 1<<1 | 1<<2 | 1<<3);
290     DDRD  &= ~(1<<2 | 1<<3);
291     PORTD &= ~(1<<2 | 1<<3);
292     DDRC  &= ~(1<<6);
293     PORTC &= ~(1<<6);
294 }
295
296 static void select_row(uint8_t row)
297 {
298     if (row < 7) {
299         // select on mcp23018
300         if (mcp23018_status) { // if there was an error
301             // do nothing
302         } else {
303             // set active row low  : 0
304             // set other rows hi-Z : 1
305             mcp23018_status = i2c_start(I2C_ADDR_WRITE);        if (mcp23018_status) goto out;
306             mcp23018_status = i2c_write(GPIOA);                 if (mcp23018_status) goto out;
307             mcp23018_status = i2c_write( 0xFF & ~(1<<row) 
308                                   & ~(ergodox_left_led_3<<LEFT_LED_3_SHIFT)
309                               );                                if (mcp23018_status) goto out;
310         out:
311             i2c_stop();
312         }
313     } else {
314         // select on teensy
315         // Output low(DDR:1, PORT:0) to select
316         switch (row) {
317             case 7:
318                 DDRB  |= (1<<0);
319                 PORTB &= ~(1<<0);
320                 break;
321             case 8:
322                 DDRB  |= (1<<1);
323                 PORTB &= ~(1<<1);
324                 break;
325             case 9:
326                 DDRB  |= (1<<2);
327                 PORTB &= ~(1<<2);
328                 break;
329             case 10:
330                 DDRB  |= (1<<3);
331                 PORTB &= ~(1<<3);
332                 break;
333             case 11:
334                 DDRD  |= (1<<2);
335                 PORTD &= ~(1<<3);
336                 break;
337             case 12:
338                 DDRD  |= (1<<3);
339                 PORTD &= ~(1<<3);
340                 break;
341             case 13:
342                 DDRC  |= (1<<6);
343                 PORTC &= ~(1<<6);
344                 break;
345         }
346     }
347 }
348