xwhatsit/modelfkeyboards_com: prevent calibration and bootmagic being enabled at...
[jackhill/qmk/firmware.git] / keyboards / xwhatsit / modelfkeyboards_com / matrix.c
CommitLineData
332fa7c4
PA
1/* Copyright 2020 Purdea Andrei
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "quantum.h"
18
405cc8cf
PA
19#define DAC_SCLK B1
20#define DAC_DIN B2
21#define DAC_SYNC_N B0
22
23#define SHIFT_DIN D4
24#define SHIFT_OE D5
405cc8cf 25#define SHIFT_SHCP D7
31b3f2de
PA
26#define SHIFT_STCP D6
27#define SHIFT_STCP_IO _SFR_IO_ADDR(PORTD)
28#define SHIFT_STCP_BIT 6
405cc8cf
PA
29
30#define KEYBOARD_SETTLE_TIME_US 8
31#define DAC_SETTLE_TIME_US 8
32
f3f721b9
PA
33#define HARDCODED_SAMPLE_TIME 4
34
31b3f2de
PA
35#define READ_ROWS_PIN_1 _SFR_IO_ADDR(PINC)
36#define READ_ROWS_PIN_2 _SFR_IO_ADDR(PIND)
37#define READ_ROWS_ASM_INSTRUCTIONS "in %[dest_row_1], %[ioreg_row_1]\n\tin %[dest_row_2], %[ioreg_row_2]"
f2a03868 38#define READ_ROWS_OUTPUT_CONSTRAINTS [dest_row_1] "=&r" (dest_row_1), [dest_row_2] "=&r" (dest_row_2)
31b3f2de
PA
39#define READ_ROWS_INPUT_CONSTRAINTS [ioreg_row_1] "I" (READ_ROWS_PIN_1), [ioreg_row_2] "I" (READ_ROWS_PIN_2)
40#define READ_ROWS_LOCAL_VARS uint8_t dest_row_1, dest_row_2
41#define READ_ROWS_VALUE ((dest_row_1 >> 4) | (dest_row_2 << 4))
42
0162369b 43static inline uint8_t read_rows(void)
405cc8cf 44{
31b3f2de
PA
45 READ_ROWS_LOCAL_VARS;
46 asm volatile (READ_ROWS_ASM_INSTRUCTIONS : READ_ROWS_OUTPUT_CONSTRAINTS : READ_ROWS_INPUT_CONSTRAINTS);
47 return READ_ROWS_VALUE;
405cc8cf
PA
48}
49
50void dac_init(void)
51{
52 setPinOutput(DAC_SCLK);
53 setPinOutput(DAC_DIN);
54 setPinOutput(DAC_SYNC_N);
55 writePin(DAC_SYNC_N, 1);
56 writePin(DAC_SCLK, 0);
57 writePin(DAC_SCLK, 1);
58 writePin(DAC_SCLK, 0);
59}
60
61void dac_write_threshold(uint16_t value)
62{
c733dd82 63 value <<= 2; // The two LSB bits of this DAC are don't care.
405cc8cf
PA
64 writePin(DAC_SYNC_N, 0);
65 int i;
66 for (i=0;i<16;i++)
67 {
68 writePin(DAC_DIN, (value >> 15) & 1);
69 value <<= 1;
70 writePin(DAC_SCLK, 1);
71 writePin(DAC_SCLK, 0);
72 }
73 writePin(DAC_SYNC_N, 1);
74 writePin(DAC_SCLK, 1);
75 writePin(DAC_SCLK, 0);
76 wait_us(DAC_SETTLE_TIME_US);
77}
78
79void shift_select_nothing(void)
80{
81 writePin(SHIFT_DIN, 0);
82 int i;
83 for (i=0;i<16;i++)
84 {
85 writePin(SHIFT_SHCP, 1);
86 writePin(SHIFT_SHCP, 0);
87 }
88 writePin(SHIFT_STCP, 1);
89 writePin(SHIFT_STCP, 0);
90}
91
31b3f2de 92void shift_select_col_no_strobe(uint8_t col)
405cc8cf
PA
93{
94 int i;
95 for (i=15; i>=0; i--)
96 {
97 writePin(SHIFT_DIN, !!(col == i));
98 writePin(SHIFT_SHCP, 1);
99 writePin(SHIFT_SHCP, 0);
100 }
405cc8cf
PA
101}
102
31b3f2de
PA
103static inline void shift_select_col(uint8_t col)
104{
105 shift_select_col_no_strobe(col);
106 writePin(SHIFT_STCP, 1);
107 writePin(SHIFT_STCP, 0);
108}
109
405cc8cf
PA
110void shift_init(void)
111{
112 setPinOutput(SHIFT_DIN);
113 setPinOutput(SHIFT_OE);
114 setPinOutput(SHIFT_STCP);
115 setPinOutput(SHIFT_SHCP);
116 writePin(SHIFT_STCP, 0);
117 writePin(SHIFT_SHCP, 0);
118 shift_select_nothing();
119 wait_us(KEYBOARD_SETTLE_TIME_US);
120}
121
31b3f2de
PA
122// the following function requires storage for 2 * (time + 1) bytes
123// but returns valid data only in the first (time + 1) bytes
124void test_multiple(uint8_t col, uint16_t time, uint8_t *array)
125{
126 shift_select_col_no_strobe(col);
127 uint16_t index;
128 READ_ROWS_LOCAL_VARS;
129 uint8_t *arrayp = array;
130 asm volatile (
131 "ldi %A[index], 0" "\n\t"
132 "ldi %B[index], 0" "\n\t"
133 "cli" "\n\t"
134 "sbi %[stcp_regaddr], %[stcp_bit]" "\n\t"
135 "1:" READ_ROWS_ASM_INSTRUCTIONS "\n\t"
136 "st %a[arr]+, %[dest_row_1]" "\n\t"
137 "st %a[arr]+, %[dest_row_2]" "\n\t"
138 "adiw %A[index], 0x01" "\n\t"
139 "cp %A[index], %A[time]" "\n\t"
140 "cpc %B[index], %B[time]" "\n\t"
141 "brlo 1b" "\n\t"
142 "sei" "\n\t"
143 "cbi %[stcp_regaddr], %[stcp_bit]" "\n\t"
144 : [arr] "=e" (arrayp),
145 [index] "=&w" (index),
146 READ_ROWS_OUTPUT_CONSTRAINTS
147 : [time] "r" (time + 1),
148 [stcp_regaddr] "I" (SHIFT_STCP_IO),
149 [stcp_bit] "I" (SHIFT_STCP_BIT),
150 READ_ROWS_INPUT_CONSTRAINTS,
151 "0" (arrayp)
152 : "memory" );
153 uint16_t i, p0, p1;
154 p0 = p1 = 0;
155 for (i=0; i<=time; i++)
156 {
157 dest_row_1 = array[p0++];
158 dest_row_2 = array[p0++];
159 array[p1++] = READ_ROWS_VALUE;
160 }
f2a03868
PA
161 shift_select_nothing();
162 wait_us(KEYBOARD_SETTLE_TIME_US);
31b3f2de
PA
163}
164
165uint8_t test_single(uint8_t col, uint16_t time)
166{
167 shift_select_col_no_strobe(col);
168 uint16_t index;
169 READ_ROWS_LOCAL_VARS;
170 uint8_t dummy_data;
171 uint8_t *arrayp = &dummy_data;
172 asm volatile (
173 "ldi %A[index], 0" "\n\t"
174 "ldi %B[index], 0" "\n\t"
175 "cli" "\n\t"
176 "sbi %[stcp_regaddr], %[stcp_bit]" "\n\t"
177 "1:" READ_ROWS_ASM_INSTRUCTIONS "\n\t"
178 "st %a[arr], %[dest_row_1]" "\n\t"
179 "st %a[arr], %[dest_row_2]" "\n\t"
180 "adiw %A[index], 0x01" "\n\t"
181 "cp %A[index], %A[time]" "\n\t"
182 "cpc %B[index], %B[time]" "\n\t"
183 "brlo 1b" "\n\t"
184 "sei" "\n\t"
185 "cbi %[stcp_regaddr], %[stcp_bit]" "\n\t"
186 : [arr] "=e" (arrayp),
187 [index] "=&w" (index),
188 READ_ROWS_OUTPUT_CONSTRAINTS
189 : [time] "r" (time + 1),
190 [stcp_regaddr] "I" (SHIFT_STCP_IO),
191 [stcp_bit] "I" (SHIFT_STCP_BIT),
192 READ_ROWS_INPUT_CONSTRAINTS,
193 "0" (arrayp)
194 : "memory" );
f2a03868
PA
195 shift_select_nothing();
196 wait_us(KEYBOARD_SETTLE_TIME_US);
31b3f2de
PA
197 return READ_ROWS_VALUE;
198}
405cc8cf 199
f2a03868
PA
200#define NRTIMES 128
201#define TESTATONCE 8
202#define REPS_V2 15
203void test_col_print_data_v2(uint8_t col)
204{
205 uprintf("%d: ", col);
206 static uint8_t data[NRTIMES*2];
207 static uint8_t sums[(TESTATONCE+1) * 8];
208 uint8_t to_time = NRTIMES-1;
209 uint8_t from_time = 0;
210 while (from_time<NRTIMES-1)
211 {
212 if (to_time - from_time + 1 > TESTATONCE)
213 {
214 to_time = from_time + TESTATONCE - 1;
215 }
216 uint8_t curr_TESTATONCE = to_time - from_time + 1;
217 uint8_t i;
218 for (i=0;i<(sizeof(sums)/sizeof(sums[0]));i++)
219 {
220 sums[i] = 0;
221 }
222 for (i=0;i<REPS_V2;i++)
223 {
224 uint8_t st = read_rows();
225 test_multiple(col, to_time, data);
226 uint8_t j;
227 uint8_t ii = 0;
228 uint8_t k;
229 for (j=0;j<curr_TESTATONCE;j++)
230 {
231 uint8_t dataj = data[j + from_time];
232 for (k=0; k<8;k++)
233 {
234 sums[ii] += (dataj & 1);
235 dataj >>= 1;
236 ii += 1;
237 }
238 }
239 if (from_time == 0) {
240 ii = TESTATONCE * 8;
241 for (k=0; k<8;k++)
242 {
243 sums[ii] += (st & 1);
244 st >>= 1;
245 ii += 1;
246 }
247 }
248 }
249 if (from_time == 0) {
250 for (i=TESTATONCE*8;i<(TESTATONCE+1)*8;i++) {
251 if (sums[i] > 0xf) {
252 print("?");
253 } else {
254 uprintf("%X", sums[i]);
255 }
256 }
257 print(":");
258 }
259 for (i=0;i<curr_TESTATONCE*8;i++)
260 {
261 if (sums[i] > 0xf) {
262 print("?");
263 } else {
264 uprintf("%X", sums[i]);
265 }
266 }
267 from_time = to_time + 1;
268 to_time = NRTIMES - 1;
269 }
270 print("\n");
271}
272
f2a03868
PA
273void test_v2(void) {
274 int i;
275 for (i=7;i>0;i--) {
276 uprintf("Starting test in %d\n", i);
277 wait_ms(1000);
278 }
279 uprintf("shift_init()");
280 shift_init();
281 uprintf(" DONE\n");
282 uprintf("dac_init()");
283 dac_init();
284 uprintf(" DONE\n");
285 int d;
286 for (d=90;d<=260;d++)
287 {
288 uprintf("Testing threshold: %d\n", d);
289 dac_write_threshold(d);
290 #if 1
291 int c;
292 for (c=0; c<10;c++)
293 {
294 test_col_print_data_v2(c);
295 }
296 test_col_print_data_v2(15);
297 #else
298 test_col_print_data_v2(0);
299 test_col_print_data_v2(2);
300 test_col_print_data_v2(6);
301 test_col_print_data_v2(7);
302 test_col_print_data_v2(15);
303 #endif
304 }
305 uprintf("TEST DONE\n");
306 while(1);
307}
308
1e57a998
PA
309#define TRACKING_TEST_TIME 4
310// Key 1 is the always non-pressed key under the space bar to the right.
311#define TRACKING_KEY_1_COL 6
312#define TRACKING_KEY_1_ROW 4
313// Key 2 is the always-pressed calibration pad to the far right-bottom of the keyboard. (both on F62 and F77)
314#define TRACKING_KEY_2_COL 15
315#define TRACKING_KEY_2_ROW 6
316// Key 3 is the F key
317#define TRACKING_KEY_3_COL 2
318#define TRACKING_KEY_3_ROW 5
319// Key 4 is the half of the split backspace that is unused if the user has a normal backspace.
320#define TRACKING_KEY_4_COL 7
321#define TRACKING_KEY_4_ROW 3
322// Key 5 is hidden key next to the left shift, which is only used in ISO layouts.
323#define TRACKING_KEY_5_COL 0
324#define TRACKING_KEY_5_ROW 7
325
326#define TRACKING_REPS 16
327
f3f721b9 328static uint16_t measure_middle(uint8_t col, uint8_t row, uint8_t time, uint8_t reps)
1e57a998 329{
f3f721b9 330 uint8_t reps_div2 = reps / 2;
1e57a998
PA
331 uint16_t min = 0, max = 1023;
332 while (min < max)
333 {
334 uint16_t mid = (min + max) / 2;
335 dac_write_threshold(mid);
336 uint8_t sum = 0;
337 uint8_t i;
f3f721b9 338 for (i=0;i<reps;i++)
1e57a998
PA
339 {
340 sum += (test_single(col, time) >> row) & 1;
341 }
f3f721b9 342 if (sum < reps_div2)
1e57a998
PA
343 {
344 max = mid - 1;
f3f721b9 345 } else if (sum > reps_div2) {
1e57a998
PA
346 min = mid + 1;
347 } else return mid;
348 }
349 return min;
350}
351
352void tracking_test(void)
353{
354 int i;
355 for (i=7;i>0;i--) {
356 uprintf("Starting test in %d\n", i);
357 wait_ms(1000);
358 }
359 uprintf("shift_init()");
360 shift_init();
361 uprintf(" DONE\n");
362 uprintf("dac_init()");
363 dac_init();
364 uprintf(" DONE\n");
365 while (1) {
f3f721b9
PA
366 uint16_t key1 = measure_middle(TRACKING_KEY_1_COL, TRACKING_KEY_1_ROW, TRACKING_TEST_TIME, TRACKING_REPS);
367 uint16_t key2 = measure_middle(TRACKING_KEY_2_COL, TRACKING_KEY_2_ROW, TRACKING_TEST_TIME, TRACKING_REPS);
368 uint16_t key3 = measure_middle(TRACKING_KEY_3_COL, TRACKING_KEY_3_ROW, TRACKING_TEST_TIME, TRACKING_REPS);
369 uint16_t key4 = measure_middle(TRACKING_KEY_4_COL, TRACKING_KEY_4_ROW, TRACKING_TEST_TIME, TRACKING_REPS);
370 uint16_t key5 = measure_middle(TRACKING_KEY_5_COL, TRACKING_KEY_5_ROW, TRACKING_TEST_TIME, TRACKING_REPS);
1e57a998
PA
371 uprintf("%u, %u, %u, %u, %u\n", key1, key2, key3, key4, key5);
372 }
373}
f2a03868 374
f3f721b9
PA
375#define KEYMAP_ROW_TO_PHYSICAL_ROW(row) (7-(row))
376#define PHYSICAL_ROW_TO_KEYMAP_ROW(row) (7-(row))
377#define KEYMAP_COL_TO_PHYSICAL_COL(col) (((col) == 10)?15:(col))
378
379uint16_t calibration_measure_all_valid_keys(uint8_t time, uint8_t reps, bool looking_for_all_zero)
380{
381 uint16_t min = 0, max = 1023;
382 while (min < max)
383 {
384 uint16_t mid = (min + max) / 2;
385 if (!looking_for_all_zero) {
386 mid = (min + max + 1) / 2;
387 }
388 dac_write_threshold(mid);
389 uint8_t col;
390 for (col = 0; col < MATRIX_COLS; col++)
391 {
392 uint8_t valid_physical_rows = 0;
393 uint8_t row;
394 for (row=0; row < MATRIX_ROWS; row++)
395 {
396 if (pgm_read_byte(&keymaps[0][row][col]) != KC_NO)
397 {
398 valid_physical_rows |= (1 << KEYMAP_ROW_TO_PHYSICAL_ROW(row)); // convert keymap row to physical row
399 }
400 }
401 uint8_t physical_col = KEYMAP_COL_TO_PHYSICAL_COL(col);
402 uint8_t i;
403 for (i=0;i<reps;i++) {
404 if (looking_for_all_zero)
405 {
406 uint8_t all_zero = (test_single(physical_col, time) & valid_physical_rows) == 0;
407 if (!all_zero) {
408 min = mid + 1;
409 goto next_binary_search;
410 }
411 } else {
412 uint8_t all_ones = (test_single(physical_col, time) & valid_physical_rows) == valid_physical_rows;
413 if (!all_ones) {
414 max = mid - 1;
415 goto next_binary_search;
416 }
417 }
418 }
419 }
420 if (looking_for_all_zero) {
421 max = mid;
422 } else {
423 min = mid;
424 }
425 next_binary_search:;
426 }
427 return min;
428}
429
430#define CAL_ENABLED
431#define CAL_DEBUG
432#define CAL_INIT_REPS 16
433#define CAL_EACHKEY_REPS 16
434#define CAL_BINS 3
435#define CAL_THRESHOLD_OFFSET 12
436
c737e37a
PA
437#if defined(CAL_ENABLED)
438#if defined(BOOTMAGIC_ENABLE) || defined(BOOTMAGIC_LITE)
439#error "Calibration is not supported in conjunction with BOOTMAGIC, because calibration requires that no keys are pressed while the keyboard is plugged in"
440#endif
441#endif
442
f3f721b9
PA
443uint16_t cal_thresholds[CAL_BINS];
444matrix_row_t assigned_to_threshold[CAL_BINS][MATRIX_ROWS];
445uint16_t cal_tr_allzero;
446uint16_t cal_tr_allone;
f3f721b9
PA
447void calibration(void)
448{
3f49a80b
PA
449 uint16_t cal_thresholds_max[CAL_BINS]={0xFFFFU,0xFFFFU,0xFFFFU};
450 uint16_t cal_thresholds_min[CAL_BINS]={0xFFFFU,0xFFFFU,0xFFFFU};
f3f721b9
PA
451 cal_tr_allzero = calibration_measure_all_valid_keys(HARDCODED_SAMPLE_TIME, CAL_INIT_REPS, true);
452 cal_tr_allone = calibration_measure_all_valid_keys(HARDCODED_SAMPLE_TIME, CAL_INIT_REPS, false);
453 uint16_t max = (cal_tr_allzero == 0) ? 0 : (cal_tr_allzero - 1);
454 uint16_t min = cal_tr_allone + 1;
455 if (max < min) max = min;
456 uint16_t d = max - min;
457 uint8_t i;
458 for (i=0;i<CAL_BINS;i++) {
459 cal_thresholds[i] = min + (d * (2 * i + 1)) / 2 / CAL_BINS;
460 }
461 uint8_t col;
462 for (col = 0; col < MATRIX_COLS; col++) {
463 uint8_t physical_col = KEYMAP_COL_TO_PHYSICAL_COL(col);
464 uint8_t row;
465 for (row = 0; row < MATRIX_ROWS; row++) {
466 if (pgm_read_byte(&keymaps[0][row][col]) != KC_NO) {
467 uint16_t threshold = measure_middle(physical_col, KEYMAP_ROW_TO_PHYSICAL_ROW(row), HARDCODED_SAMPLE_TIME, CAL_EACHKEY_REPS);
468 uint8_t besti = 0;
469 uint16_t best_diff = (uint16_t)abs(threshold - cal_thresholds[besti]);
470 for (i=1;i<CAL_BINS;i++) {
471 uint16_t this_diff = (uint16_t)abs(threshold - cal_thresholds[i]);
472 if (this_diff < best_diff)
473 {
474 best_diff = this_diff;
475 besti = i;
476 }
477 }
478 assigned_to_threshold[besti][row] |= (1 << col);
3f49a80b
PA
479 if ((cal_thresholds_max[besti] = 0xFFFFU) || (cal_thresholds_max[besti] < threshold)) cal_thresholds_max[besti] = threshold;
480 if ((cal_thresholds_min[besti] = 0xFFFFU) || (cal_thresholds_min[besti] > threshold)) cal_thresholds_min[besti] = threshold;
f3f721b9
PA
481 }
482 }
483 }
484 for (i=0;i<CAL_BINS;i++) {
3f49a80b
PA
485 if ((cal_thresholds_max[i] == 0xFFFFU) || (cal_thresholds_min[i] == 0xFFFFU)) {
486 cal_thresholds[i] += CAL_THRESHOLD_OFFSET;
487 } else {
488 cal_thresholds[i] = (cal_thresholds_max[i] + cal_thresholds_min[i]) / 2 + CAL_THRESHOLD_OFFSET;
489 }
f3f721b9
PA
490 }
491}
492
09c2b24e
PA
493void real_keyboard_init_basic(void)
494{
495 uprintf("shift_init()");
496 shift_init();
497 uprintf(" DONE\n");
498 uprintf("dac_init()");
499 dac_init();
500 uprintf(" DONE\n");
f3f721b9
PA
501 #if defined(CAL_ENABLED)
502 calibration();
503 #else
c733dd82 504 dac_write_threshold(142);
9d9305db
PA
505 dac_write_threshold(142);
506 dac_write_threshold(142);
f3f721b9 507 #endif
09c2b24e
PA
508}
509
06b963f3 510void matrix_init_custom(void) {
9d9305db 511 //test_v2();
1e57a998 512 //tracking_test();
09c2b24e 513 real_keyboard_init_basic();
06b963f3
PA
514}
515
f3f721b9
PA
516matrix_row_t previous_matrix[MATRIX_ROWS];
517#if defined(CAL_ENABLED) && defined(CAL_DEBUG)
518bool cal_stats_printed = false;
519#endif
dabf024c 520
332fa7c4 521bool matrix_scan_custom(matrix_row_t current_matrix[]) {
f3f721b9
PA
522 uint8_t col, row, cal;
523 #if defined(CAL_ENABLED) && defined(CAL_DEBUG)
524 if (!cal_stats_printed)
525 {
526 uint32_t time = timer_read32();
527 if (time >= 10 * 1000UL) { // after 10 seconds
528 uprintf("Cal All Zero = %u, Cal All Ones = %u\n", cal_tr_allzero, cal_tr_allone);
529 for (cal=0;cal<CAL_BINS;cal++)
530 {
531 uprintf("Cal bin %u, Threshold=%u Assignments:\n", cal, cal_thresholds[cal]);
532 for (row=0;row<MATRIX_ROWS;row++)
533 {
534 uprintf("0x%02X\n", assigned_to_threshold[cal][row]);
535 }
536 }
537 cal_stats_printed = true;
538 }
539 }
540 #endif
09c2b24e
PA
541 for (row=0;row<8;row++)
542 {
543 current_matrix[row] = 0;
544 }
f3f721b9
PA
545 #if defined(CAL_ENABLED)
546 for (cal=0;cal<CAL_BINS;cal++) {
547 dac_write_threshold(cal_thresholds[cal]);
548 for (col=0;col<MATRIX_COLS;col++) {
549 uint8_t real_col = KEYMAP_COL_TO_PHYSICAL_COL(col);
550 uint8_t d;
551 uint8_t d_tested = 0;
552 for (row=0;row<MATRIX_ROWS;row++) {
553 if (assigned_to_threshold[cal][row] & (1 << col))
554 {
555 if (!d_tested)
556 {
557 d = test_single(real_col, HARDCODED_SAMPLE_TIME);
558 d_tested = 1;
559 }
560 uint8_t physical_row = KEYMAP_ROW_TO_PHYSICAL_ROW(row);
561 current_matrix[row] |= ((d >> physical_row) & 1) << col;
562 }
563 }
564 }
565 }
566 #else
567 for (col=0;col<MATRIX_COLS;col++)
09c2b24e 568 {
f3f721b9
PA
569 uint8_t real_col = KEYMAP_COL_TO_PHYSICAL_COL(col);
570 uint8_t d = test_single(real_col, HARDCODED_SAMPLE_TIME);
571 for (row=0;row<MATRIX_ROWS;row++)
09c2b24e 572 {
f3f721b9 573 current_matrix[PHYSICAL_ROW_TO_KEYMAP_ROW(row)] |= (((uint16_t)(d & 1)) << col);
09c2b24e
PA
574 d >>= 1;
575 }
576 }
f3f721b9
PA
577 #endif
578 bool changed = false;
579 for (row=0;row<MATRIX_ROWS;row++)
580 {
581 if (previous_matrix[row] != current_matrix[row]) changed = true;
582 previous_matrix[row] = current_matrix[row];
583 }
dabf024c 584 return changed;
332fa7c4 585}