]> git.donarmstrong.com Git - qmk_firmware.git/blob - tmk_core/common/keyboard.c
[Core] Optimize matrix processing (#7621)
[qmk_firmware.git] / tmk_core / common / keyboard.c
1 /*
2 Copyright 2011, 2012, 2013 Jun Wako <wakojun@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 #include <stdint.h>
19 #include "keyboard.h"
20 #include "matrix.h"
21 #include "keymap.h"
22 #include "host.h"
23 #include "led.h"
24 #include "keycode.h"
25 #include "timer.h"
26 #include "print.h"
27 #include "debug.h"
28 #include "command.h"
29 #include "util.h"
30 #include "sendchar.h"
31 #include "eeconfig.h"
32 #include "action_layer.h"
33 #ifdef BACKLIGHT_ENABLE
34 #    include "backlight.h"
35 #endif
36 #ifdef BOOTMAGIC_ENABLE
37 #    include "bootmagic.h"
38 #else
39 #    include "magic.h"
40 #endif
41 #ifdef MOUSEKEY_ENABLE
42 #    include "mousekey.h"
43 #endif
44 #ifdef PS2_MOUSE_ENABLE
45 #    include "ps2_mouse.h"
46 #endif
47 #ifdef SERIAL_MOUSE_ENABLE
48 #    include "serial_mouse.h"
49 #endif
50 #ifdef ADB_MOUSE_ENABLE
51 #    include "adb.h"
52 #endif
53 #ifdef RGBLIGHT_ENABLE
54 #    include "rgblight.h"
55 #endif
56 #ifdef STENO_ENABLE
57 #    include "process_steno.h"
58 #endif
59 #ifdef FAUXCLICKY_ENABLE
60 #    include "fauxclicky.h"
61 #endif
62 #ifdef SERIAL_LINK_ENABLE
63 #    include "serial_link/system/serial_link.h"
64 #endif
65 #ifdef VISUALIZER_ENABLE
66 #    include "visualizer/visualizer.h"
67 #endif
68 #ifdef POINTING_DEVICE_ENABLE
69 #    include "pointing_device.h"
70 #endif
71 #ifdef MIDI_ENABLE
72 #    include "process_midi.h"
73 #endif
74 #ifdef HD44780_ENABLE
75 #    include "hd44780.h"
76 #endif
77 #ifdef QWIIC_ENABLE
78 #    include "qwiic.h"
79 #endif
80 #ifdef OLED_DRIVER_ENABLE
81 #    include "oled_driver.h"
82 #endif
83 #ifdef VELOCIKEY_ENABLE
84 #    include "velocikey.h"
85 #endif
86
87 // Only enable this if console is enabled to print to
88 #if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE)
89 static uint32_t matrix_timer      = 0;
90 static uint32_t matrix_scan_count = 0;
91
92 void matrix_scan_perf_task(void) {
93     matrix_scan_count++;
94
95     uint32_t timer_now = timer_read32();
96     if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) {
97         dprintf("matrix scan frequency: %d\n", matrix_scan_count);
98
99         matrix_timer      = timer_now;
100         matrix_scan_count = 0;
101     }
102 }
103 #else
104 #    define matrix_scan_perf_task()
105 #endif
106
107 #ifdef MATRIX_HAS_GHOST
108 extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS];
109 static matrix_row_t   get_real_keys(uint8_t row, matrix_row_t rowdata) {
110     matrix_row_t out = 0;
111     for (uint8_t col = 0; col < MATRIX_COLS; col++) {
112         // read each key in the row data and check if the keymap defines it as a real key
113         if (pgm_read_byte(&keymaps[0][row][col]) && (rowdata & (1 << col))) {
114             // this creates new row data, if a key is defined in the keymap, it will be set here
115             out |= 1 << col;
116         }
117     }
118     return out;
119 }
120
121 static inline bool popcount_more_than_one(matrix_row_t rowdata) {
122     rowdata &= rowdata - 1;  // if there are less than two bits (keys) set, rowdata will become zero
123     return rowdata;
124 }
125
126 static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) {
127     /* No ghost exists when less than 2 keys are down on the row.
128     If there are "active" blanks in the matrix, the key can't be pressed by the user,
129     there is no doubt as to which keys are really being pressed.
130     The ghosts will be ignored, they are KC_NO.   */
131     rowdata = get_real_keys(row, rowdata);
132     if ((popcount_more_than_one(rowdata)) == 0) {
133         return false;
134     }
135     /* Ghost occurs when the row shares a column line with other row,
136     and two columns are read on each row. Blanks in the matrix don't matter,
137     so they are filtered out.
138     If there are two or more real keys pressed and they match columns with
139     at least two of another row's real keys, the row will be ignored. Keep in mind,
140     we are checking one row at a time, not all of them at once.
141     */
142     for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
143         if (i != row && popcount_more_than_one(get_real_keys(i, matrix_get_row(i)) & rowdata)) {
144             return true;
145         }
146     }
147     return false;
148 }
149
150 #endif
151
152 void disable_jtag(void) {
153 // To use PF4-7 (PC2-5 on ATmega32A), disable JTAG by writing JTD bit twice within four cycles.
154 #if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
155     MCUCR |= _BV(JTD);
156     MCUCR |= _BV(JTD);
157 #elif defined(__AVR_ATmega32A__)
158     MCUCSR |= _BV(JTD);
159     MCUCSR |= _BV(JTD);
160 #endif
161 }
162
163 /** \brief matrix_setup
164  *
165  * FIXME: needs doc
166  */
167 __attribute__((weak)) void matrix_setup(void) {}
168
169 /** \brief keyboard_pre_init_user
170  *
171  * FIXME: needs doc
172  */
173 __attribute__((weak)) void keyboard_pre_init_user(void) {}
174
175 /** \brief keyboard_pre_init_kb
176  *
177  * FIXME: needs doc
178  */
179 __attribute__((weak)) void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); }
180
181 /** \brief keyboard_post_init_user
182  *
183  * FIXME: needs doc
184  */
185
186 __attribute__((weak)) void keyboard_post_init_user() {}
187
188 /** \brief keyboard_post_init_kb
189  *
190  * FIXME: needs doc
191  */
192
193 __attribute__((weak)) void keyboard_post_init_kb(void) { keyboard_post_init_user(); }
194
195 /** \brief keyboard_setup
196  *
197  * FIXME: needs doc
198  */
199 void keyboard_setup(void) {
200 #ifndef NO_JTAG_DISABLE
201     disable_jtag();
202 #endif
203     matrix_setup();
204     keyboard_pre_init_kb();
205 }
206
207 /** \brief is_keyboard_master
208  *
209  * FIXME: needs doc
210  */
211 __attribute__((weak)) bool is_keyboard_master(void) { return true; }
212
213 /** \brief keyboard_init
214  *
215  * FIXME: needs doc
216  */
217 void keyboard_init(void) {
218     timer_init();
219     matrix_init();
220 #ifdef QWIIC_ENABLE
221     qwiic_init();
222 #endif
223 #ifdef OLED_DRIVER_ENABLE
224     oled_init(OLED_ROTATION_0);
225 #endif
226 #ifdef PS2_MOUSE_ENABLE
227     ps2_mouse_init();
228 #endif
229 #ifdef SERIAL_MOUSE_ENABLE
230     serial_mouse_init();
231 #endif
232 #ifdef ADB_MOUSE_ENABLE
233     adb_mouse_init();
234 #endif
235 #ifdef BOOTMAGIC_ENABLE
236     bootmagic();
237 #else
238     magic();
239 #endif
240 #ifdef BACKLIGHT_ENABLE
241     backlight_init();
242 #endif
243 #ifdef RGBLIGHT_ENABLE
244     rgblight_init();
245 #endif
246 #ifdef STENO_ENABLE
247     steno_init();
248 #endif
249 #ifdef FAUXCLICKY_ENABLE
250     fauxclicky_init();
251 #endif
252 #ifdef POINTING_DEVICE_ENABLE
253     pointing_device_init();
254 #endif
255 #if defined(NKRO_ENABLE) && defined(FORCE_NKRO)
256     keymap_config.nkro = 1;
257     eeconfig_update_keymap(keymap_config.raw);
258 #endif
259     keyboard_post_init_kb(); /* Always keep this last */
260 }
261
262 /** \brief Keyboard task: Do keyboard routine jobs
263  *
264  * Do routine keyboard jobs:
265  *
266  * * scan matrix
267  * * handle mouse movements
268  * * run visualizer code
269  * * handle midi commands
270  * * light LEDs
271  *
272  * This is repeatedly called as fast as possible.
273  */
274 void keyboard_task(void) {
275     static matrix_row_t matrix_prev[MATRIX_ROWS];
276     static uint8_t      led_status    = 0;
277     matrix_row_t        matrix_row    = 0;
278     matrix_row_t        matrix_change = 0;
279 #ifdef QMK_KEYS_PER_SCAN
280     uint8_t keys_processed = 0;
281 #endif
282
283 #if defined(OLED_DRIVER_ENABLE) && !defined(OLED_DISABLE_TIMEOUT)
284     uint8_t ret = matrix_scan();
285 #else
286     matrix_scan();
287 #endif
288
289     if (is_keyboard_master()) {
290         for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
291             matrix_row    = matrix_get_row(r);
292             matrix_change = matrix_row ^ matrix_prev[r];
293             if (matrix_change) {
294 #ifdef MATRIX_HAS_GHOST
295                 if (has_ghost_in_row(r, matrix_row)) {
296                     continue;
297                 }
298 #endif
299                 if (debug_matrix) matrix_print();
300                 matrix_row_t col_mask = 1;
301                 for (uint8_t c = 0; c < MATRIX_COLS; c++, col_mask <<= 1) {
302                     if (matrix_change & col_mask) {
303                         action_exec((keyevent_t){
304                             .key = (keypos_t){.row = r, .col = c}, .pressed = (matrix_row & col_mask), .time = (timer_read() | 1) /* time should not be 0 */
305                         });
306                         // record a processed key
307                         matrix_prev[r] ^= col_mask;
308 #ifdef QMK_KEYS_PER_SCAN
309                         // only jump out if we have processed "enough" keys.
310                         if (++keys_processed >= QMK_KEYS_PER_SCAN)
311 #endif
312                             // process a key per task call
313                             goto MATRIX_LOOP_END;
314                     }
315                 }
316             }
317         }
318     }
319     // call with pseudo tick event when no real key event.
320 #ifdef QMK_KEYS_PER_SCAN
321     // we can get here with some keys processed now.
322     if (!keys_processed)
323 #endif
324         action_exec(TICK);
325
326 MATRIX_LOOP_END:
327
328 #ifdef DEBUG_MATRIX_SCAN_RATE
329     matrix_scan_perf_task();
330 #endif
331
332 #ifdef QWIIC_ENABLE
333     qwiic_task();
334 #endif
335
336 #ifdef OLED_DRIVER_ENABLE
337     oled_task();
338 #    ifndef OLED_DISABLE_TIMEOUT
339     // Wake up oled if user is using those fabulous keys!
340     if (ret) oled_on();
341 #    endif
342 #endif
343
344 #ifdef MOUSEKEY_ENABLE
345     // mousekey repeat & acceleration
346     mousekey_task();
347 #endif
348
349 #ifdef PS2_MOUSE_ENABLE
350     ps2_mouse_task();
351 #endif
352
353 #ifdef SERIAL_MOUSE_ENABLE
354     serial_mouse_task();
355 #endif
356
357 #ifdef ADB_MOUSE_ENABLE
358     adb_mouse_task();
359 #endif
360
361 #ifdef SERIAL_LINK_ENABLE
362     serial_link_update();
363 #endif
364
365 #ifdef VISUALIZER_ENABLE
366     visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds());
367 #endif
368
369 #ifdef POINTING_DEVICE_ENABLE
370     pointing_device_task();
371 #endif
372
373 #ifdef MIDI_ENABLE
374     midi_task();
375 #endif
376
377 #ifdef VELOCIKEY_ENABLE
378     if (velocikey_enabled()) {
379         velocikey_decelerate();
380     }
381 #endif
382
383     // update LED
384     if (led_status != host_keyboard_leds()) {
385         led_status = host_keyboard_leds();
386         keyboard_set_leds(led_status);
387     }
388 }
389
390 /** \brief keyboard set leds
391  *
392  * FIXME: needs doc
393  */
394 void keyboard_set_leds(uint8_t leds) {
395     if (debug_keyboard) {
396         debug("keyboard_set_led: ");
397         debug_hex8(leds);
398         debug("\n");
399     }
400     led_set(leds);
401 }