]> git.donarmstrong.com Git - qmk_firmware.git/blob - tmk_core/common/keyboard.c
Optimize matrix scanning (#343)
[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 #include <stdint.h>
18 #include "keyboard.h"
19 #include "matrix.h"
20 #include "keymap.h"
21 #include "host.h"
22 #include "led.h"
23 #include "keycode.h"
24 #include "timer.h"
25 #include "print.h"
26 #include "debug.h"
27 #include "command.h"
28 #include "util.h"
29 #include "sendchar.h"
30 #include "eeconfig.h"
31 #include "backlight.h"
32 #ifdef BOOTMAGIC_ENABLE
33 #   include "bootmagic.h"
34 #else
35 #   include "magic.h"
36 #endif
37 #ifdef MOUSEKEY_ENABLE
38 #   include "mousekey.h"
39 #endif
40 #ifdef PS2_MOUSE_ENABLE
41 #   include "ps2_mouse.h"
42 #endif
43 #ifdef SERIAL_MOUSE_ENABLE
44 #   include "serial_mouse.h"
45 #endif
46 #ifdef ADB_MOUSE_ENABLE
47 #   include "adb.h"
48 #endif
49
50 #ifdef MATRIX_HAS_GHOST
51 static bool is_row_ghosting(uint8_t row){
52     matrix_row_t state = matrix_get_row(row);
53     /* no ghosting happens when only one key in the row is pressed */
54     if (!(state - 1 & state)) return false;
55     /* ghosting occurs when two keys in the same column are pressed */
56     for (int8_t r = MATRIX_ROWS - 1; r >= 0; --r) {
57         if (r != row && matrix_get_row(r) & state) return true;
58     }
59     return false;
60 }
61
62 #endif
63
64 __attribute__ ((weak))
65 void matrix_setup(void) {
66 }
67
68 void keyboard_setup(void) {
69     matrix_setup();
70 }
71
72 void keyboard_init(void) {
73     timer_init();
74     matrix_init();
75 #ifdef PS2_MOUSE_ENABLE
76     ps2_mouse_init();
77 #endif
78 #ifdef SERIAL_MOUSE_ENABLE
79     serial_mouse_init();
80 #endif
81 #ifdef ADB_MOUSE_ENABLE
82     adb_mouse_init();
83 #endif
84 #ifdef BOOTMAGIC_ENABLE
85     bootmagic();
86 #else
87     magic();
88 #endif
89 #ifdef BACKLIGHT_ENABLE
90     backlight_init();
91 #endif
92 #if defined(NKRO_ENABLE) && defined(FORCE_NKRO)
93         keyboard_nkro = true;
94 #endif
95 }
96
97 /* does routine keyboard jobs */
98 void keyboard_task(void) {
99     static matrix_row_t previous_matrix[MATRIX_ROWS];
100 #ifdef MATRIX_HAS_GHOST
101     static matrix_row_t deghosting_matrix[MATRIX_ROWS];
102 #endif
103     static uint8_t led_status = 0;
104     matrix_scan();
105     for (int8_t r = MATRIX_ROWS - 1; r >= 0; --r) {
106         matrix_row_t state = matrix_get_row(r);
107         matrix_row_t changes = state ^ previous_matrix[r];
108         if (changes) {
109 #ifdef MATRIX_HAS_GHOST
110             if (is_row_ghosting(r)) {
111                 /* debugs the deghosting mechanism */
112                 /* doesn't update previous_matrix until the ghosting has stopped
113                  * in order to prevent the last key from being lost
114                  */
115                 if (debug_matrix && deghosting_matrix[r] != state) {
116                     matrix_print();
117                 }
118                 deghosting_matrix[r] = state;
119                 continue;
120             }
121             deghosting_matrix[r] = state;
122 #endif
123             if (debug_matrix) matrix_print();
124             for (int8_t c = MATRIX_COLS - 1; c >= 0; --c) {
125                 matrix_row_t mask = (matrix_row_t)1 << c;
126                 if (changes & mask) {
127                     keyevent_t event;
128                     event.key = (keypos_t){ .row = r, .col = c };
129                     event.pressed = state & mask;
130                     /* the time should not be 0 */
131                     event.time = timer_read() | 1;
132                     action_exec(event);
133                     /* records the processed key event */
134                     previous_matrix[r] ^= mask;
135                     /* processes one key event per call */
136                     goto event_processed;
137                 }
138             }
139         }
140     }
141     /* sends tick events when the keyboard is idle */
142     action_exec(TICK);
143 event_processed:
144 #ifdef MOUSEKEY_ENABLE
145     /* repeats and accelerates the mouse keys */
146     mousekey_task();
147 #endif
148 #ifdef PS2_MOUSE_ENABLE
149     ps2_mouse_task();
150 #endif
151 #ifdef SERIAL_MOUSE_ENABLE
152     serial_mouse_task();
153 #endif
154 #ifdef ADB_MOUSE_ENABLE
155     adb_mouse_task();
156 #endif
157     /* updates the LEDs */
158     if (led_status != host_keyboard_leds()) {
159         led_status = host_keyboard_leds();
160         keyboard_set_leds(led_status);
161     }
162 }
163
164 void keyboard_set_leds(uint8_t leds) {
165     if (debug_keyboard) dprintf("Keyboard LEDs state: %x\n", leds);
166     led_set(leds);
167 }