]> git.donarmstrong.com Git - tmk_firmware.git/blob - keyboard/ergodox/matrix.c
e50932c92c25ffc752fc4a7098d5cb6e82c2599f
[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     ergodox_board_led_off();
109     ergodox_left_led_1_off();
110     ergodox_left_led_2_off();
111     ergodox_left_led_3_off();
112     switch (layer) {
113         case 1:
114             // all
115             ergodox_left_led_1_on();
116             ergodox_left_led_2_on();
117             ergodox_left_led_3_on();
118             break;
119         case 2:
120             // blue
121             ergodox_left_led_2_on();
122             break;
123         case 8:
124             // blue and green
125             ergodox_left_led_2_on();
126             // break missed intentionally
127         case 3:
128             // green
129             ergodox_left_led_3_on();
130             break;
131         case 6:
132             ergodox_board_led_on();
133             // break missed intentionally
134         case 4:
135         case 5:
136         case 7:
137             // red
138             ergodox_left_led_1_on();
139             break;
140         default:
141             // none
142             break;
143     }
144
145     mcp23018_status = ergodox_left_leds_update();
146 #endif
147
148     for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
149         select_row(i);
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         _delay_us(30);  // without this wait read unstable value.
250         // read from teensy
251         return
252             (PINF&(1<<0) ? 0 : (1<<0)) |
253             (PINF&(1<<1) ? 0 : (1<<1)) |
254             (PINF&(1<<4) ? 0 : (1<<2)) |
255             (PINF&(1<<5) ? 0 : (1<<3)) |
256             (PINF&(1<<6) ? 0 : (1<<4)) |
257             (PINF&(1<<7) ? 0 : (1<<5)) ;
258     }
259 }
260
261 /* Row pin configuration
262  *
263  * Teensy
264  * row: 7   8   9   10  11  12  13
265  * pin: B0  B1  B2  B3  D2  D3  C6
266  *
267  * MCP23018
268  * row: 0   1   2   3   4   5   6
269  * pin: A0  A1  A2  A3  A4  A5  A6
270  */
271 static void unselect_rows(void)
272 {
273     // unselect on mcp23018
274     if (mcp23018_status) { // if there was an error
275         // do nothing
276     } else {
277         // set all rows hi-Z : 1
278         mcp23018_status = i2c_start(I2C_ADDR_WRITE);    if (mcp23018_status) goto out;
279         mcp23018_status = i2c_write(GPIOA);             if (mcp23018_status) goto out;
280         mcp23018_status = i2c_write( 0xFF
281                               & ~(ergodox_left_led_3<<LEFT_LED_3_SHIFT)
282                           );                            if (mcp23018_status) goto out;
283     out:
284         i2c_stop();
285     }
286
287     // unselect on teensy
288     // Hi-Z(DDR:0, PORT:0) to unselect
289     DDRB  &= ~(1<<0 | 1<<1 | 1<<2 | 1<<3);
290     PORTB &= ~(1<<0 | 1<<1 | 1<<2 | 1<<3);
291     DDRD  &= ~(1<<2 | 1<<3);
292     PORTD &= ~(1<<2 | 1<<3);
293     DDRC  &= ~(1<<6);
294     PORTC &= ~(1<<6);
295 }
296
297 static void select_row(uint8_t row)
298 {
299     if (row < 7) {
300         // select on mcp23018
301         if (mcp23018_status) { // if there was an error
302             // do nothing
303         } else {
304             // set active row low  : 0
305             // set other rows hi-Z : 1
306             mcp23018_status = i2c_start(I2C_ADDR_WRITE);        if (mcp23018_status) goto out;
307             mcp23018_status = i2c_write(GPIOA);                 if (mcp23018_status) goto out;
308             mcp23018_status = i2c_write( 0xFF & ~(1<<row) 
309                                   & ~(ergodox_left_led_3<<LEFT_LED_3_SHIFT)
310                               );                                if (mcp23018_status) goto out;
311         out:
312             i2c_stop();
313         }
314     } else {
315         // select on teensy
316         // Output low(DDR:1, PORT:0) to select
317         switch (row) {
318             case 7:
319                 DDRB  |= (1<<0);
320                 PORTB &= ~(1<<0);
321                 break;
322             case 8:
323                 DDRB  |= (1<<1);
324                 PORTB &= ~(1<<1);
325                 break;
326             case 9:
327                 DDRB  |= (1<<2);
328                 PORTB &= ~(1<<2);
329                 break;
330             case 10:
331                 DDRB  |= (1<<3);
332                 PORTB &= ~(1<<3);
333                 break;
334             case 11:
335                 DDRD  |= (1<<2);
336                 PORTD &= ~(1<<3);
337                 break;
338             case 12:
339                 DDRD  |= (1<<3);
340                 PORTD &= ~(1<<3);
341                 break;
342             case 13:
343                 DDRC  |= (1<<6);
344                 PORTC &= ~(1<<6);
345                 break;
346         }
347     }
348 }
349