aboutsummaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
Diffstat (limited to 'quantum')
-rw-r--r--quantum/debounce.h2
-rw-r--r--quantum/debounce/none.c31
-rw-r--r--quantum/debounce/sym_defer_g.c26
-rw-r--r--quantum/debounce/sym_defer_pk.c67
-rw-r--r--quantum/debounce/sym_eager_pk.c72
-rw-r--r--quantum/debounce/sym_eager_pr.c76
-rw-r--r--quantum/debounce/tests/debounce_test_common.cpp229
-rw-r--r--quantum/debounce/tests/debounce_test_common.h83
-rw-r--r--quantum/debounce/tests/rules.mk39
-rw-r--r--quantum/debounce/tests/sym_defer_g_tests.cpp223
-rw-r--r--quantum/debounce/tests/sym_defer_pk_tests.cpp225
-rw-r--r--quantum/debounce/tests/sym_eager_pk_tests.cpp237
-rw-r--r--quantum/debounce/tests/sym_eager_pr_tests.cpp280
-rw-r--r--quantum/debounce/tests/testlist.mk5
-rw-r--r--quantum/led_matrix.c4
-rw-r--r--quantum/matrix.c79
-rw-r--r--quantum/rgb_matrix.c4
-rw-r--r--quantum/rgb_matrix.h2
-rw-r--r--quantum/rgb_matrix_drivers.c16
-rw-r--r--quantum/split_common/matrix.c77
20 files changed, 1596 insertions, 181 deletions
diff --git a/quantum/debounce.h b/quantum/debounce.h
index 9ca05c682..504386828 100644
--- a/quantum/debounce.h
+++ b/quantum/debounce.h
@@ -9,3 +9,5 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
9bool debounce_active(void); 9bool debounce_active(void);
10 10
11void debounce_init(uint8_t num_rows); 11void debounce_init(uint8_t num_rows);
12
13void debounce_free(void);
diff --git a/quantum/debounce/none.c b/quantum/debounce/none.c
new file mode 100644
index 000000000..b03892bc5
--- /dev/null
+++ b/quantum/debounce/none.c
@@ -0,0 +1,31 @@
1/* Copyright 2021 Simon Arlott
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 "matrix.h"
18#include "quantum.h"
19#include <stdlib.h>
20
21void debounce_init(uint8_t num_rows) {}
22
23void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
24 for (int i = 0; i < num_rows; i++) {
25 cooked[i] = raw[i];
26 }
27}
28
29bool debounce_active(void) { return false; }
30
31void debounce_free(void) {}
diff --git a/quantum/debounce/sym_defer_g.c b/quantum/debounce/sym_defer_g.c
index 3ed9055d2..fbefd55ed 100644
--- a/quantum/debounce/sym_defer_g.c
+++ b/quantum/debounce/sym_defer_g.c
@@ -1,5 +1,6 @@
1/* 1/*
2Copyright 2017 Alex Ong<the.onga@gmail.com> 2Copyright 2017 Alex Ong<the.onga@gmail.com>
3Copyright 2021 Simon Arlott
3This program is free software: you can redistribute it and/or modify 4This program is free software: you can redistribute it and/or modify
4it under the terms of the GNU General Public License as published by 5it under the terms of the GNU General Public License as published by
5the Free Software Foundation, either version 2 of the License, or 6the Free Software Foundation, either version 2 of the License, or
@@ -23,30 +24,29 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
23# define DEBOUNCE 5 24# define DEBOUNCE 5
24#endif 25#endif
25 26
26void debounce_init(uint8_t num_rows) {} 27#if DEBOUNCE > 0
27static bool debouncing = false; 28static bool debouncing = false;
29static fast_timer_t debouncing_time;
28 30
29#if DEBOUNCE > 0 31void debounce_init(uint8_t num_rows) {}
30static uint16_t debouncing_time; 32
31void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { 33void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
32 if (changed) { 34 if (changed) {
33 debouncing = true; 35 debouncing = true;
34 debouncing_time = timer_read(); 36 debouncing_time = timer_read_fast();
35 } 37 }
36 38
37 if (debouncing && timer_elapsed(debouncing_time) > DEBOUNCE) { 39 if (debouncing && timer_elapsed_fast(debouncing_time) >= DEBOUNCE) {
38 for (int i = 0; i < num_rows; i++) { 40 for (int i = 0; i < num_rows; i++) {
39 cooked[i] = raw[i]; 41 cooked[i] = raw[i];
40 } 42 }
41 debouncing = false; 43 debouncing = false;
42 } 44 }
43} 45}
44#else // no debouncing.
45void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
46 for (int i = 0; i < num_rows; i++) {
47 cooked[i] = raw[i];
48 }
49}
50#endif
51 46
52bool debounce_active(void) { return debouncing; } 47bool debounce_active(void) { return debouncing; }
48
49void debounce_free(void) {}
50#else // no debouncing.
51# include "none.c"
52#endif
diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c
index 60513f98e..626a9be84 100644
--- a/quantum/debounce/sym_defer_pk.c
+++ b/quantum/debounce/sym_defer_pk.c
@@ -1,6 +1,7 @@
1/* 1/*
2Copyright 2017 Alex Ong<the.onga@gmail.com> 2Copyright 2017 Alex Ong<the.onga@gmail.com>
3Copyright 2020 Andrei Purdea<andrei@purdea.ro> 3Copyright 2020 Andrei Purdea<andrei@purdea.ro>
4Copyright 2021 Simon Arlott
4This program is free software: you can redistribute it and/or modify 5This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by 6it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or 7the Free Software Foundation, either version 2 of the License, or
@@ -33,28 +34,25 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
33# define DEBOUNCE 5 34# define DEBOUNCE 5
34#endif 35#endif
35 36
37// Maximum debounce: 255ms
38#if DEBOUNCE > UINT8_MAX
39# undef DEBOUNCE
40# define DEBOUNCE UINT8_MAX
41#endif
42
36#define ROW_SHIFTER ((matrix_row_t)1) 43#define ROW_SHIFTER ((matrix_row_t)1)
37 44
38#define debounce_counter_t uint8_t 45typedef uint8_t debounce_counter_t;
39 46
47#if DEBOUNCE > 0
40static debounce_counter_t *debounce_counters; 48static debounce_counter_t *debounce_counters;
49static fast_timer_t last_time;
41static bool counters_need_update; 50static bool counters_need_update;
42 51
43#define DEBOUNCE_ELAPSED 251 52#define DEBOUNCE_ELAPSED 0
44#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1)
45
46static uint8_t wrapping_timer_read(void) {
47 static uint16_t time = 0;
48 static uint8_t last_result = 0;
49 uint16_t new_time = timer_read();
50 uint16_t diff = new_time - time;
51 time = new_time;
52 last_result = (last_result + diff) % (MAX_DEBOUNCE + 1);
53 return last_result;
54}
55 53
56void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); 54static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
57void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); 55static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
58 56
59// we use num_rows rather than MATRIX_ROWS to support split keyboards 57// we use num_rows rather than MATRIX_ROWS to support split keyboards
60void debounce_init(uint8_t num_rows) { 58void debounce_init(uint8_t num_rows) {
@@ -67,27 +65,49 @@ void debounce_init(uint8_t num_rows) {
67 } 65 }
68} 66}
69 67
68void debounce_free(void) {
69 free(debounce_counters);
70 debounce_counters = NULL;
71}
72
70void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { 73void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
71 uint8_t current_time = wrapping_timer_read(); 74 bool updated_last = false;
75
72 if (counters_need_update) { 76 if (counters_need_update) {
73 update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, current_time); 77 fast_timer_t now = timer_read_fast();
78 fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
79
80 last_time = now;
81 updated_last = true;
82 if (elapsed_time > UINT8_MAX) {
83 elapsed_time = UINT8_MAX;
84 }
85
86 if (elapsed_time > 0) {
87 update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time);
88 }
74 } 89 }
75 90
76 if (changed) { 91 if (changed) {
77 start_debounce_counters(raw, cooked, num_rows, current_time); 92 if (!updated_last) {
93 last_time = timer_read_fast();
94 }
95
96 start_debounce_counters(raw, cooked, num_rows);
78 } 97 }
79} 98}
80 99
81void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { 100static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) {
82 counters_need_update = false; 101 counters_need_update = false;
83 debounce_counter_t *debounce_pointer = debounce_counters; 102 debounce_counter_t *debounce_pointer = debounce_counters;
84 for (uint8_t row = 0; row < num_rows; row++) { 103 for (uint8_t row = 0; row < num_rows; row++) {
85 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 104 for (uint8_t col = 0; col < MATRIX_COLS; col++) {
86 if (*debounce_pointer != DEBOUNCE_ELAPSED) { 105 if (*debounce_pointer != DEBOUNCE_ELAPSED) {
87 if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { 106 if (*debounce_pointer <= elapsed_time) {
88 *debounce_pointer = DEBOUNCE_ELAPSED; 107 *debounce_pointer = DEBOUNCE_ELAPSED;
89 cooked[row] = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col)); 108 cooked[row] = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col));
90 } else { 109 } else {
110 *debounce_pointer -= elapsed_time;
91 counters_need_update = true; 111 counters_need_update = true;
92 } 112 }
93 } 113 }
@@ -96,14 +116,14 @@ void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix
96 } 116 }
97} 117}
98 118
99void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { 119static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
100 debounce_counter_t *debounce_pointer = debounce_counters; 120 debounce_counter_t *debounce_pointer = debounce_counters;
101 for (uint8_t row = 0; row < num_rows; row++) { 121 for (uint8_t row = 0; row < num_rows; row++) {
102 matrix_row_t delta = raw[row] ^ cooked[row]; 122 matrix_row_t delta = raw[row] ^ cooked[row];
103 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 123 for (uint8_t col = 0; col < MATRIX_COLS; col++) {
104 if (delta & (ROW_SHIFTER << col)) { 124 if (delta & (ROW_SHIFTER << col)) {
105 if (*debounce_pointer == DEBOUNCE_ELAPSED) { 125 if (*debounce_pointer == DEBOUNCE_ELAPSED) {
106 *debounce_pointer = current_time; 126 *debounce_pointer = DEBOUNCE;
107 counters_need_update = true; 127 counters_need_update = true;
108 } 128 }
109 } else { 129 } else {
@@ -115,3 +135,6 @@ void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t
115} 135}
116 136
117bool debounce_active(void) { return true; } 137bool debounce_active(void) { return true; }
138#else
139# include "none.c"
140#endif
diff --git a/quantum/debounce/sym_eager_pk.c b/quantum/debounce/sym_eager_pk.c
index e66cf92d7..15a3242e6 100644
--- a/quantum/debounce/sym_eager_pk.c
+++ b/quantum/debounce/sym_eager_pk.c
@@ -1,5 +1,6 @@
1/* 1/*
2Copyright 2017 Alex Ong<the.onga@gmail.com> 2Copyright 2017 Alex Ong<the.onga@gmail.com>
3Copyright 2021 Simon Arlott
3This program is free software: you can redistribute it and/or modify 4This program is free software: you can redistribute it and/or modify
4it under the terms of the GNU General Public License as published by 5it under the terms of the GNU General Public License as published by
5the Free Software Foundation, either version 2 of the License, or 6the Free Software Foundation, either version 2 of the License, or
@@ -33,29 +34,26 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
33# define DEBOUNCE 5 34# define DEBOUNCE 5
34#endif 35#endif
35 36
37// Maximum debounce: 255ms
38#if DEBOUNCE > UINT8_MAX
39# undef DEBOUNCE
40# define DEBOUNCE UINT8_MAX
41#endif
42
36#define ROW_SHIFTER ((matrix_row_t)1) 43#define ROW_SHIFTER ((matrix_row_t)1)
37 44
38#define debounce_counter_t uint8_t 45typedef uint8_t debounce_counter_t;
39 46
47#if DEBOUNCE > 0
40static debounce_counter_t *debounce_counters; 48static debounce_counter_t *debounce_counters;
49static fast_timer_t last_time;
41static bool counters_need_update; 50static bool counters_need_update;
42static bool matrix_need_update; 51static bool matrix_need_update;
43 52
44#define DEBOUNCE_ELAPSED 251 53#define DEBOUNCE_ELAPSED 0
45#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1)
46
47static uint8_t wrapping_timer_read(void) {
48 static uint16_t time = 0;
49 static uint8_t last_result = 0;
50 uint16_t new_time = timer_read();
51 uint16_t diff = new_time - time;
52 time = new_time;
53 last_result = (last_result + diff) % (MAX_DEBOUNCE + 1);
54 return last_result;
55}
56 54
57void update_debounce_counters(uint8_t num_rows, uint8_t current_time); 55static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
58void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); 56static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
59 57
60// we use num_rows rather than MATRIX_ROWS to support split keyboards 58// we use num_rows rather than MATRIX_ROWS to support split keyboards
61void debounce_init(uint8_t num_rows) { 59void debounce_init(uint8_t num_rows) {
@@ -68,27 +66,51 @@ void debounce_init(uint8_t num_rows) {
68 } 66 }
69} 67}
70 68
69void debounce_free(void) {
70 free(debounce_counters);
71 debounce_counters = NULL;
72}
73
71void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { 74void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
72 uint8_t current_time = wrapping_timer_read(); 75 bool updated_last = false;
76
73 if (counters_need_update) { 77 if (counters_need_update) {
74 update_debounce_counters(num_rows, current_time); 78 fast_timer_t now = timer_read_fast();
79 fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
80
81 last_time = now;
82 updated_last = true;
83 if (elapsed_time > UINT8_MAX) {
84 elapsed_time = UINT8_MAX;
85 }
86
87 if (elapsed_time > 0) {
88 update_debounce_counters(num_rows, elapsed_time);
89 }
75 } 90 }
76 91
77 if (changed || matrix_need_update) { 92 if (changed || matrix_need_update) {
78 transfer_matrix_values(raw, cooked, num_rows, current_time); 93 if (!updated_last) {
94 last_time = timer_read_fast();
95 }
96
97 transfer_matrix_values(raw, cooked, num_rows);
79 } 98 }
80} 99}
81 100
82// If the current time is > debounce counter, set the counter to enable input. 101// If the current time is > debounce counter, set the counter to enable input.
83void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { 102static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {
84 counters_need_update = false; 103 counters_need_update = false;
104 matrix_need_update = false;
85 debounce_counter_t *debounce_pointer = debounce_counters; 105 debounce_counter_t *debounce_pointer = debounce_counters;
86 for (uint8_t row = 0; row < num_rows; row++) { 106 for (uint8_t row = 0; row < num_rows; row++) {
87 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 107 for (uint8_t col = 0; col < MATRIX_COLS; col++) {
88 if (*debounce_pointer != DEBOUNCE_ELAPSED) { 108 if (*debounce_pointer != DEBOUNCE_ELAPSED) {
89 if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { 109 if (*debounce_pointer <= elapsed_time) {
90 *debounce_pointer = DEBOUNCE_ELAPSED; 110 *debounce_pointer = DEBOUNCE_ELAPSED;
111 matrix_need_update = true;
91 } else { 112 } else {
113 *debounce_pointer -= elapsed_time;
92 counters_need_update = true; 114 counters_need_update = true;
93 } 115 }
94 } 116 }
@@ -98,8 +120,7 @@ void update_debounce_counters(uint8_t num_rows, uint8_t current_time) {
98} 120}
99 121
100// upload from raw_matrix to final matrix; 122// upload from raw_matrix to final matrix;
101void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { 123static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
102 matrix_need_update = false;
103 debounce_counter_t *debounce_pointer = debounce_counters; 124 debounce_counter_t *debounce_pointer = debounce_counters;
104 for (uint8_t row = 0; row < num_rows; row++) { 125 for (uint8_t row = 0; row < num_rows; row++) {
105 matrix_row_t delta = raw[row] ^ cooked[row]; 126 matrix_row_t delta = raw[row] ^ cooked[row];
@@ -108,11 +129,9 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n
108 matrix_row_t col_mask = (ROW_SHIFTER << col); 129 matrix_row_t col_mask = (ROW_SHIFTER << col);
109 if (delta & col_mask) { 130 if (delta & col_mask) {
110 if (*debounce_pointer == DEBOUNCE_ELAPSED) { 131 if (*debounce_pointer == DEBOUNCE_ELAPSED) {
111 *debounce_pointer = current_time; 132 *debounce_pointer = DEBOUNCE;
112 counters_need_update = true; 133 counters_need_update = true;
113 existing_row ^= col_mask; // flip the bit. 134 existing_row ^= col_mask; // flip the bit.
114 } else {
115 matrix_need_update = true;
116 } 135 }
117 } 136 }
118 debounce_pointer++; 137 debounce_pointer++;
@@ -122,3 +141,6 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n
122} 141}
123 142
124bool debounce_active(void) { return true; } 143bool debounce_active(void) { return true; }
144#else
145# include "none.c"
146#endif
diff --git a/quantum/debounce/sym_eager_pr.c b/quantum/debounce/sym_eager_pr.c
index 20ccb46f1..2ad592c5a 100644
--- a/quantum/debounce/sym_eager_pr.c
+++ b/quantum/debounce/sym_eager_pr.c
@@ -1,5 +1,6 @@
1/* 1/*
2Copyright 2019 Alex Ong<the.onga@gmail.com> 2Copyright 2019 Alex Ong<the.onga@gmail.com>
3Copyright 2021 Simon Arlott
3This program is free software: you can redistribute it and/or modify 4This program is free software: you can redistribute it and/or modify
4it under the terms of the GNU General Public License as published by 5it under the terms of the GNU General Public License as published by
5the Free Software Foundation, either version 2 of the License, or 6the Free Software Foundation, either version 2 of the License, or
@@ -33,27 +34,25 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
33# define DEBOUNCE 5 34# define DEBOUNCE 5
34#endif 35#endif
35 36
36#define debounce_counter_t uint8_t 37// Maximum debounce: 255ms
38#if DEBOUNCE > UINT8_MAX
39# undef DEBOUNCE
40# define DEBOUNCE UINT8_MAX
41#endif
42
43typedef uint8_t debounce_counter_t;
44
45#if DEBOUNCE > 0
37static bool matrix_need_update; 46static bool matrix_need_update;
38 47
39static debounce_counter_t *debounce_counters; 48static debounce_counter_t *debounce_counters;
49static fast_timer_t last_time;
40static bool counters_need_update; 50static bool counters_need_update;
41 51
42#define DEBOUNCE_ELAPSED 251 52#define DEBOUNCE_ELAPSED 0
43#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1)
44
45static uint8_t wrapping_timer_read(void) {
46 static uint16_t time = 0;
47 static uint8_t last_result = 0;
48 uint16_t new_time = timer_read();
49 uint16_t diff = new_time - time;
50 time = new_time;
51 last_result = (last_result + diff) % (MAX_DEBOUNCE + 1);
52 return last_result;
53}
54 53
55void update_debounce_counters(uint8_t num_rows, uint8_t current_time); 54static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
56void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); 55static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
57 56
58// we use num_rows rather than MATRIX_ROWS to support split keyboards 57// we use num_rows rather than MATRIX_ROWS to support split keyboards
59void debounce_init(uint8_t num_rows) { 58void debounce_init(uint8_t num_rows) {
@@ -63,27 +62,50 @@ void debounce_init(uint8_t num_rows) {
63 } 62 }
64} 63}
65 64
65void debounce_free(void) {
66 free(debounce_counters);
67 debounce_counters = NULL;
68}
69
66void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { 70void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
67 uint8_t current_time = wrapping_timer_read(); 71 bool updated_last = false;
68 bool needed_update = counters_need_update; 72
69 if (counters_need_update) { 73 if (counters_need_update) {
70 update_debounce_counters(num_rows, current_time); 74 fast_timer_t now = timer_read_fast();
75 fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
76
77 last_time = now;
78 updated_last = true;
79 if (elapsed_time > UINT8_MAX) {
80 elapsed_time = UINT8_MAX;
81 }
82
83 if (elapsed_time > 0) {
84 update_debounce_counters(num_rows, elapsed_time);
85 }
71 } 86 }
72 87
73 if (changed || (needed_update && !counters_need_update) || matrix_need_update) { 88 if (changed || matrix_need_update) {
74 transfer_matrix_values(raw, cooked, num_rows, current_time); 89 if (!updated_last) {
90 last_time = timer_read_fast();
91 }
92
93 transfer_matrix_values(raw, cooked, num_rows);
75 } 94 }
76} 95}
77 96
78// If the current time is > debounce counter, set the counter to enable input. 97// If the current time is > debounce counter, set the counter to enable input.
79void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { 98static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {
80 counters_need_update = false; 99 counters_need_update = false;
100 matrix_need_update = false;
81 debounce_counter_t *debounce_pointer = debounce_counters; 101 debounce_counter_t *debounce_pointer = debounce_counters;
82 for (uint8_t row = 0; row < num_rows; row++) { 102 for (uint8_t row = 0; row < num_rows; row++) {
83 if (*debounce_pointer != DEBOUNCE_ELAPSED) { 103 if (*debounce_pointer != DEBOUNCE_ELAPSED) {
84 if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { 104 if (*debounce_pointer <= elapsed_time) {
85 *debounce_pointer = DEBOUNCE_ELAPSED; 105 *debounce_pointer = DEBOUNCE_ELAPSED;
106 matrix_need_update = true;
86 } else { 107 } else {
108 *debounce_pointer -= elapsed_time;
87 counters_need_update = true; 109 counters_need_update = true;
88 } 110 }
89 } 111 }
@@ -92,8 +114,7 @@ void update_debounce_counters(uint8_t num_rows, uint8_t current_time) {
92} 114}
93 115
94// upload from raw_matrix to final matrix; 116// upload from raw_matrix to final matrix;
95void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { 117static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
96 matrix_need_update = false;
97 debounce_counter_t *debounce_pointer = debounce_counters; 118 debounce_counter_t *debounce_pointer = debounce_counters;
98 for (uint8_t row = 0; row < num_rows; row++) { 119 for (uint8_t row = 0; row < num_rows; row++) {
99 matrix_row_t existing_row = cooked[row]; 120 matrix_row_t existing_row = cooked[row];
@@ -102,11 +123,9 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n
102 // determine new value basd on debounce pointer + raw value 123 // determine new value basd on debounce pointer + raw value
103 if (existing_row != raw_row) { 124 if (existing_row != raw_row) {
104 if (*debounce_pointer == DEBOUNCE_ELAPSED) { 125 if (*debounce_pointer == DEBOUNCE_ELAPSED) {
105 *debounce_pointer = current_time; 126 *debounce_pointer = DEBOUNCE;
106 cooked[row] = raw_row; 127 cooked[row] = raw_row;
107 counters_need_update = true; 128 counters_need_update = true;
108 } else {
109 matrix_need_update = true;
110 } 129 }
111 } 130 }
112 debounce_pointer++; 131 debounce_pointer++;
@@ -114,3 +133,6 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n
114} 133}
115 134
116bool debounce_active(void) { return true; } 135bool debounce_active(void) { return true; }
136#else
137# include "none.c"
138#endif
diff --git a/quantum/debounce/tests/debounce_test_common.cpp b/quantum/debounce/tests/debounce_test_common.cpp
new file mode 100644
index 000000000..1c5e7c9f4
--- /dev/null
+++ b/quantum/debounce/tests/debounce_test_common.cpp
@@ -0,0 +1,229 @@
1/* Copyright 2021 Simon Arlott
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 "gtest/gtest.h"
18
19#include "debounce_test_common.h"
20
21#include <algorithm>
22#include <iomanip>
23#include <sstream>
24
25extern "C" {
26#include "quantum.h"
27#include "timer.h"
28#include "debounce.h"
29
30void set_time(uint32_t t);
31void advance_time(uint32_t ms);
32}
33
34void DebounceTest::addEvents(std::initializer_list<DebounceTestEvent> events) {
35 events_.insert(events_.end(), events.begin(), events.end());
36}
37
38void DebounceTest::runEvents() {
39 /* Run the test multiple times, from 1kHz to 10kHz scan rate */
40 for (extra_iterations_ = 0; extra_iterations_ < 10; extra_iterations_++) {
41 if (time_jumps_) {
42 /* Don't advance time smoothly, jump to the next event (some tests require this) */
43 auto_advance_time_ = false;
44 runEventsInternal();
45 } else {
46 /* Run the test with both smooth and irregular time; it must produce the same result */
47 auto_advance_time_ = true;
48 runEventsInternal();
49 auto_advance_time_ = false;
50 runEventsInternal();
51 }
52 }
53}
54
55void DebounceTest::runEventsInternal() {
56 fast_timer_t previous = 0;
57 bool first = true;
58
59 /* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */
60 debounce_init(MATRIX_ROWS);
61 set_time(time_offset_);
62 std::fill(std::begin(input_matrix_), std::end(input_matrix_), 0);
63 std::fill(std::begin(output_matrix_), std::end(output_matrix_), 0);
64
65 for (auto &event : events_) {
66 if (!auto_advance_time_) {
67 /* Jump to the next event */
68 set_time(time_offset_ + event.time_);
69 } else if (!first && event.time_ == previous + 1) {
70 /* This event immediately follows the previous one, don't make extra debounce() calls */
71 advance_time(1);
72 } else {
73 /* Fast forward to the time for this event, calling debounce() with no changes */
74 ASSERT_LT((time_offset_ + event.time_) - timer_read_fast(), 60000) << "Test tries to advance more than 1 minute of time";
75
76 while (timer_read_fast() != time_offset_ + event.time_) {
77 runDebounce(false);
78 checkCookedMatrix(false, "debounce() modified cooked matrix");
79 advance_time(1);
80 }
81 }
82
83 first = false;
84 previous = event.time_;
85
86 /* Prepare input matrix */
87 for (auto &input : event.inputs_) {
88 matrixUpdate(input_matrix_, "input", input);
89 }
90
91 /* Call debounce */
92 runDebounce(!event.inputs_.empty());
93
94 /* Prepare output matrix */
95 for (auto &output : event.outputs_) {
96 matrixUpdate(output_matrix_, "output", output);
97 }
98
99 /* Check output matrix has expected change events */
100 for (auto &output : event.outputs_) {
101 EXPECT_EQ(!!(cooked_matrix_[output.row_] & (1U << output.col_)), directionValue(output.direction_))
102 << "Missing event at " << strTime()
103 << " expected key " << output.row_ << "," << output.col_ << " " << directionLabel(output.direction_)
104 << "\ninput_matrix: changed=" << !event.inputs_.empty() << "\n" << strMatrix(input_matrix_)
105 << "\nexpected_matrix:\n" << strMatrix(output_matrix_)
106 << "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
107 }
108
109 /* Check output matrix has no other changes */
110 checkCookedMatrix(!event.inputs_.empty(), "debounce() cooked matrix does not match expected output matrix");
111
112 /* Perform some extra iterations of the matrix scan with no changes */
113 for (int i = 0; i < extra_iterations_; i++) {
114 runDebounce(false);
115 checkCookedMatrix(false, "debounce() modified cooked matrix");
116 }
117 }
118
119 /* Check that no further changes happen for 1 minute */
120 for (int i = 0; i < 60000; i++) {
121 runDebounce(false);
122 checkCookedMatrix(false, "debounce() modified cooked matrix");
123 advance_time(1);
124 }
125
126 debounce_free();
127}
128
129void DebounceTest::runDebounce(bool changed) {
130 std::copy(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_));
131 std::copy(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_));
132
133 debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed);
134
135 if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) {
136 FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime()
137 << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_)
138 << "\nraw_matrix:\n" << strMatrix(raw_matrix_);
139 }
140}
141
142void DebounceTest::checkCookedMatrix(bool changed, const std::string &error_message) {
143 if (!std::equal(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_))) {
144 FAIL() << "Unexpected event: " << error_message << " at " << strTime()
145 << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_)
146 << "\nexpected_matrix:\n" << strMatrix(output_matrix_)
147 << "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
148 }
149}
150
151std::string DebounceTest::strTime() {
152 std::stringstream text;
153
154 text << "time " << (timer_read_fast() - time_offset_)
155 << " (extra_iterations=" << extra_iterations_
156 << ", auto_advance_time=" << auto_advance_time_ << ")";
157
158 return text.str();
159}
160
161std::string DebounceTest::strMatrix(matrix_row_t matrix[]) {
162 std::stringstream text;
163
164 text << "\t" << std::setw(3) << "";
165 for (int col = 0; col < MATRIX_COLS; col++) {
166 text << " " << std::setw(2) << col;
167 }
168 text << "\n";
169
170 for (int row = 0; row < MATRIX_ROWS; row++) {
171 text << "\t" << std::setw(2) << row << ":";
172 for (int col = 0; col < MATRIX_COLS; col++) {
173 text << ((matrix[row] & (1U << col)) ? " XX" : " __");
174 }
175
176 text << "\n";
177 }
178
179 return text.str();
180}
181
182bool DebounceTest::directionValue(Direction direction) {
183 switch (direction) {
184 case DOWN:
185 return true;
186
187 case UP:
188 return false;
189 }
190}
191
192std::string DebounceTest::directionLabel(Direction direction) {
193 switch (direction) {
194 case DOWN:
195 return "DOWN";
196
197 case UP:
198 return "UP";
199 }
200}
201
202/* Modify a matrix and verify that events always specify a change */
203void DebounceTest::matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event) {
204 ASSERT_NE(!!(matrix[event.row_] & (1U << event.col_)), directionValue(event.direction_))
205 << "Test " << name << " at " << strTime()
206 << " sets key " << event.row_ << "," << event.col_ << " " << directionLabel(event.direction_)
207 << " but it is already " << directionLabel(event.direction_)
208 << "\n" << name << "_matrix:\n" << strMatrix(matrix);
209
210 switch (event.direction_) {
211 case DOWN:
212 matrix[event.row_] |= (1U << event.col_);
213 break;
214
215 case UP:
216 matrix[event.row_] &= ~(1U << event.col_);
217 break;
218 }
219}
220
221DebounceTestEvent::DebounceTestEvent(fast_timer_t time,
222 std::initializer_list<MatrixTestEvent> inputs,
223 std::initializer_list<MatrixTestEvent> outputs)
224 : time_(time), inputs_(inputs), outputs_(outputs) {
225}
226
227MatrixTestEvent::MatrixTestEvent(int row, int col, Direction direction)
228 : row_(row), col_(col), direction_(direction) {
229}
diff --git a/quantum/debounce/tests/debounce_test_common.h b/quantum/debounce/tests/debounce_test_common.h
new file mode 100644
index 000000000..d87e31059
--- /dev/null
+++ b/quantum/debounce/tests/debounce_test_common.h
@@ -0,0 +1,83 @@
1/* Copyright 2021 Simon Arlott
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 "gtest/gtest.h"
18
19#include <initializer_list>
20#include <list>
21#include <string>
22
23extern "C" {
24#include "quantum.h"
25#include "timer.h"
26}
27
28enum Direction {
29 DOWN,
30 UP,
31};
32
33class MatrixTestEvent {
34public:
35 MatrixTestEvent(int row, int col, Direction direction);
36
37 const int row_;
38 const int col_;
39 const Direction direction_;
40};
41
42class DebounceTestEvent {
43public:
44 // 0, {{0, 1, DOWN}}, {{0, 1, DOWN}})
45 DebounceTestEvent(fast_timer_t time,
46 std::initializer_list<MatrixTestEvent> inputs,
47 std::initializer_list<MatrixTestEvent> outputs);
48
49 const fast_timer_t time_;
50 const std::list<MatrixTestEvent> inputs_;
51 const std::list<MatrixTestEvent> outputs_;
52};
53
54class DebounceTest : public ::testing::Test {
55protected:
56 void addEvents(std::initializer_list<DebounceTestEvent> events);
57 void runEvents();
58
59 fast_timer_t time_offset_ = 7777;
60 bool time_jumps_ = false;
61
62private:
63 static bool directionValue(Direction direction);
64 static std::string directionLabel(Direction direction);
65
66 void runEventsInternal();
67 void runDebounce(bool changed);
68 void checkCookedMatrix(bool changed, const std::string &error_message);
69 void matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event);
70
71 std::string strTime();
72 std::string strMatrix(matrix_row_t matrix[]);
73
74 std::list<DebounceTestEvent> events_;
75
76 matrix_row_t input_matrix_[MATRIX_ROWS];
77 matrix_row_t raw_matrix_[MATRIX_ROWS];
78 matrix_row_t cooked_matrix_[MATRIX_ROWS];
79 matrix_row_t output_matrix_[MATRIX_ROWS];
80
81 int extra_iterations_;
82 bool auto_advance_time_;
83};
diff --git a/quantum/debounce/tests/rules.mk b/quantum/debounce/tests/rules.mk
new file mode 100644
index 000000000..29fda7889
--- /dev/null
+++ b/quantum/debounce/tests/rules.mk
@@ -0,0 +1,39 @@
1# Copyright 2021 Simon Arlott
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
16DEBOUNCE_COMMON_DEFS := -DMATRIX_ROWS=4 -DMATRIX_COLS=10 -DDEBOUNCE=5
17
18DEBOUNCE_COMMON_SRC := $(QUANTUM_PATH)/debounce/tests/debounce_test_common.cpp \
19 $(TMK_PATH)/common/test/timer.c
20
21debounce_sym_defer_g_DEFS := $(DEBOUNCE_COMMON_DEFS)
22debounce_sym_defer_g_SRC := $(DEBOUNCE_COMMON_SRC) \
23 $(QUANTUM_PATH)/debounce/sym_defer_g.c \
24 $(QUANTUM_PATH)/debounce/tests/sym_defer_g_tests.cpp
25
26debounce_sym_defer_pk_DEFS := $(DEBOUNCE_COMMON_DEFS)
27debounce_sym_defer_pk_SRC := $(DEBOUNCE_COMMON_SRC) \
28 $(QUANTUM_PATH)/debounce/sym_defer_pk.c \
29 $(QUANTUM_PATH)/debounce/tests/sym_defer_pk_tests.cpp
30
31debounce_sym_eager_pk_DEFS := $(DEBOUNCE_COMMON_DEFS)
32debounce_sym_eager_pk_SRC := $(DEBOUNCE_COMMON_SRC) \
33 $(QUANTUM_PATH)/debounce/sym_eager_pk.c \
34 $(QUANTUM_PATH)/debounce/tests/sym_eager_pk_tests.cpp
35
36debounce_sym_eager_pr_DEFS := $(DEBOUNCE_COMMON_DEFS)
37debounce_sym_eager_pr_SRC := $(DEBOUNCE_COMMON_SRC) \
38 $(QUANTUM_PATH)/debounce/sym_eager_pr.c \
39 $(QUANTUM_PATH)/debounce/tests/sym_eager_pr_tests.cpp
diff --git a/quantum/debounce/tests/sym_defer_g_tests.cpp b/quantum/debounce/tests/sym_defer_g_tests.cpp
new file mode 100644
index 000000000..a56aecd8f
--- /dev/null
+++ b/quantum/debounce/tests/sym_defer_g_tests.cpp
@@ -0,0 +1,223 @@
1/* Copyright 2021 Simon Arlott
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 "gtest/gtest.h"
18
19#include "debounce_test_common.h"
20
21TEST_F(DebounceTest, OneKeyShort1) {
22 addEvents({ /* Time, Inputs, Outputs */
23 {0, {{0, 1, DOWN}}, {}},
24
25 {5, {}, {{0, 1, DOWN}}},
26 /* 0ms delay (fast scan rate) */
27 {5, {{0, 1, UP}}, {}},
28
29 {10, {}, {{0, 1, UP}}},
30 });
31 runEvents();
32}
33
34TEST_F(DebounceTest, OneKeyShort2) {
35 addEvents({ /* Time, Inputs, Outputs */
36 {0, {{0, 1, DOWN}}, {}},
37
38 {5, {}, {{0, 1, DOWN}}},
39 /* 1ms delay */
40 {6, {{0, 1, UP}}, {}},
41
42 {11, {}, {{0, 1, UP}}},
43 });
44 runEvents();
45}
46
47TEST_F(DebounceTest, OneKeyShort3) {
48 addEvents({ /* Time, Inputs, Outputs */
49 {0, {{0, 1, DOWN}}, {}},
50
51 {5, {}, {{0, 1, DOWN}}},
52 /* 2ms delay */
53 {7, {{0, 1, UP}}, {}},
54
55 {12, {}, {{0, 1, UP}}},
56 });
57 runEvents();
58}
59
60TEST_F(DebounceTest, OneKeyTooQuick1) {
61 addEvents({ /* Time, Inputs, Outputs */
62 {0, {{0, 1, DOWN}}, {}},
63 /* Release key exactly on the debounce time */
64 {5, {{0, 1, UP}}, {}},
65 });
66 runEvents();
67}
68
69TEST_F(DebounceTest, OneKeyTooQuick2) {
70 addEvents({ /* Time, Inputs, Outputs */
71 {0, {{0, 1, DOWN}}, {}},
72
73 {5, {}, {{0, 1, DOWN}}},
74 {6, {{0, 1, UP}}, {}},
75
76 /* Press key exactly on the debounce time */
77 {11, {{0, 1, DOWN}}, {}},
78 });
79 runEvents();
80}
81
82TEST_F(DebounceTest, OneKeyBouncing1) {
83 addEvents({ /* Time, Inputs, Outputs */
84 {0, {{0, 1, DOWN}}, {}},
85 {1, {{0, 1, UP}}, {}},
86 {2, {{0, 1, DOWN}}, {}},
87 {3, {{0, 1, UP}}, {}},
88 {4, {{0, 1, DOWN}}, {}},
89 {5, {{0, 1, UP}}, {}},
90 {6, {{0, 1, DOWN}}, {}},
91 {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */
92 });
93 runEvents();
94}
95
96TEST_F(DebounceTest, OneKeyBouncing2) {
97 addEvents({ /* Time, Inputs, Outputs */
98 {0, {{0, 1, DOWN}}, {}},
99 {5, {}, {{0, 1, DOWN}}},
100 {6, {{0, 1, UP}}, {}},
101 {7, {{0, 1, DOWN}}, {}},
102 {8, {{0, 1, UP}}, {}},
103 {9, {{0, 1, DOWN}}, {}},
104 {10, {{0, 1, UP}}, {}},
105 {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */
106 });
107 runEvents();
108}
109
110TEST_F(DebounceTest, OneKeyLong) {
111 addEvents({ /* Time, Inputs, Outputs */
112 {0, {{0, 1, DOWN}}, {}},
113
114 {5, {}, {{0, 1, DOWN}}},
115
116 {25, {{0, 1, UP}}, {}},
117
118 {30, {}, {{0, 1, UP}}},
119
120 {50, {{0, 1, DOWN}}, {}},
121
122 {55, {}, {{0, 1, DOWN}}},
123 });
124 runEvents();
125}
126
127TEST_F(DebounceTest, TwoKeysShort) {
128 addEvents({ /* Time, Inputs, Outputs */
129 {0, {{0, 1, DOWN}}, {}},
130 {1, {{0, 2, DOWN}}, {}},
131
132 {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
133
134 {7, {{0, 1, UP}}, {}},
135 {8, {{0, 2, UP}}, {}},
136
137 {13, {}, {{0, 1, UP}, {0, 2, UP}}},
138 });
139 runEvents();
140}
141
142TEST_F(DebounceTest, TwoKeysSimultaneous1) {
143 addEvents({ /* Time, Inputs, Outputs */
144 {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},
145
146 {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
147 {6, {{0, 1, UP}, {0, 2, UP}}, {}},
148
149 {11, {}, {{0, 1, UP}, {0, 2, UP}}},
150 });
151 runEvents();
152}
153
154TEST_F(DebounceTest, TwoKeysSimultaneous2) {
155 addEvents({ /* Time, Inputs, Outputs */
156 {0, {{0, 1, DOWN}}, {}},
157 {1, {{0, 2, DOWN}}, {}},
158
159 {5, {}, {}},
160 {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
161 {7, {{0, 1, UP}}, {}},
162 {8, {{0, 2, UP}}, {}},
163
164 {13, {}, {{0, 1, UP}, {0, 2, UP}}},
165 });
166 runEvents();
167}
168
169TEST_F(DebounceTest, OneKeyDelayedScan1) {
170 addEvents({ /* Time, Inputs, Outputs */
171 {0, {{0, 1, DOWN}}, {}},
172
173 /* Processing is very late */
174 {300, {}, {{0, 1, DOWN}}},
175 /* Immediately release key */
176 {300, {{0, 1, UP}}, {}},
177
178 {305, {}, {{0, 1, UP}}},
179 });
180 time_jumps_ = true;
181 runEvents();
182}
183
184TEST_F(DebounceTest, OneKeyDelayedScan2) {
185 addEvents({ /* Time, Inputs, Outputs */
186 {0, {{0, 1, DOWN}}, {}},
187
188 /* Processing is very late */
189 {300, {}, {{0, 1, DOWN}}},
190 /* Release key after 1ms */
191 {301, {{0, 1, UP}}, {}},
192
193 {306, {}, {{0, 1, UP}}},
194 });
195 time_jumps_ = true;
196 runEvents();
197}
198
199TEST_F(DebounceTest, OneKeyDelayedScan3) {
200 addEvents({ /* Time, Inputs, Outputs */
201 {0, {{0, 1, DOWN}}, {}},
202
203 /* Release key before debounce expires */
204 {300, {{0, 1, UP}}, {}},
205 });
206 time_jumps_ = true;
207 runEvents();
208}
209
210TEST_F(DebounceTest, OneKeyDelayedScan4) {
211 addEvents({ /* Time, Inputs, Outputs */
212 {0, {{0, 1, DOWN}}, {}},
213
214 /* Processing is a bit late */
215 {50, {}, {{0, 1, DOWN}}},
216 /* Release key after 1ms */
217 {51, {{0, 1, UP}}, {}},
218
219 {56, {}, {{0, 1, UP}}},
220 });
221 time_jumps_ = true;
222 runEvents();
223}
diff --git a/quantum/debounce/tests/sym_defer_pk_tests.cpp b/quantum/debounce/tests/sym_defer_pk_tests.cpp
new file mode 100644
index 000000000..1f3061e59
--- /dev/null
+++ b/quantum/debounce/tests/sym_defer_pk_tests.cpp
@@ -0,0 +1,225 @@
1/* Copyright 2021 Simon Arlott
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 "gtest/gtest.h"
18
19#include "debounce_test_common.h"
20
21TEST_F(DebounceTest, OneKeyShort1) {
22 addEvents({ /* Time, Inputs, Outputs */
23 {0, {{0, 1, DOWN}}, {}},
24
25 {5, {}, {{0, 1, DOWN}}},
26 /* 0ms delay (fast scan rate) */
27 {5, {{0, 1, UP}}, {}},
28
29 {10, {}, {{0, 1, UP}}},
30 });
31 runEvents();
32}
33
34TEST_F(DebounceTest, OneKeyShort2) {
35 addEvents({ /* Time, Inputs, Outputs */
36 {0, {{0, 1, DOWN}}, {}},
37
38 {5, {}, {{0, 1, DOWN}}},
39 /* 1ms delay */
40 {6, {{0, 1, UP}}, {}},
41
42 {11, {}, {{0, 1, UP}}},
43 });
44 runEvents();
45}
46
47TEST_F(DebounceTest, OneKeyShort3) {
48 addEvents({ /* Time, Inputs, Outputs */
49 {0, {{0, 1, DOWN}}, {}},
50
51 {5, {}, {{0, 1, DOWN}}},
52 /* 2ms delay */
53 {7, {{0, 1, UP}}, {}},
54
55 {12, {}, {{0, 1, UP}}},
56 });
57 runEvents();
58}
59
60TEST_F(DebounceTest, OneKeyTooQuick1) {
61 addEvents({ /* Time, Inputs, Outputs */
62 {0, {{0, 1, DOWN}}, {}},
63 /* Release key exactly on the debounce time */
64 {5, {{0, 1, UP}}, {}},
65 });
66 runEvents();
67}
68
69TEST_F(DebounceTest, OneKeyTooQuick2) {
70 addEvents({ /* Time, Inputs, Outputs */
71 {0, {{0, 1, DOWN}}, {}},
72
73 {5, {}, {{0, 1, DOWN}}},
74 {6, {{0, 1, UP}}, {}},
75
76 /* Press key exactly on the debounce time */
77 {11, {{0, 1, DOWN}}, {}},
78 });
79 runEvents();
80}
81
82TEST_F(DebounceTest, OneKeyBouncing1) {
83 addEvents({ /* Time, Inputs, Outputs */
84 {0, {{0, 1, DOWN}}, {}},
85 {1, {{0, 1, UP}}, {}},
86 {2, {{0, 1, DOWN}}, {}},
87 {3, {{0, 1, UP}}, {}},
88 {4, {{0, 1, DOWN}}, {}},
89 {5, {{0, 1, UP}}, {}},
90 {6, {{0, 1, DOWN}}, {}},
91 {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */
92 });
93 runEvents();
94}
95
96TEST_F(DebounceTest, OneKeyBouncing2) {
97 addEvents({ /* Time, Inputs, Outputs */
98 {0, {{0, 1, DOWN}}, {}},
99 {5, {}, {{0, 1, DOWN}}},
100 {6, {{0, 1, UP}}, {}},
101 {7, {{0, 1, DOWN}}, {}},
102 {8, {{0, 1, UP}}, {}},
103 {9, {{0, 1, DOWN}}, {}},
104 {10, {{0, 1, UP}}, {}},
105 {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */
106 });
107 runEvents();
108}
109
110TEST_F(DebounceTest, OneKeyLong) {
111 addEvents({ /* Time, Inputs, Outputs */
112 {0, {{0, 1, DOWN}}, {}},
113
114 {5, {}, {{0, 1, DOWN}}},
115
116 {25, {{0, 1, UP}}, {}},
117
118 {30, {}, {{0, 1, UP}}},
119
120 {50, {{0, 1, DOWN}}, {}},
121
122 {55, {}, {{0, 1, DOWN}}},
123 });
124 runEvents();
125}
126
127TEST_F(DebounceTest, TwoKeysShort) {
128 addEvents({ /* Time, Inputs, Outputs */
129 {0, {{0, 1, DOWN}}, {}},
130 {1, {{0, 2, DOWN}}, {}},
131
132 {5, {}, {{0, 1, DOWN}}},
133 {6, {}, {{0, 2, DOWN}}},
134
135 {7, {{0, 1, UP}}, {}},
136 {8, {{0, 2, UP}}, {}},
137
138 {12, {}, {{0, 1, UP}}},
139 {13, {}, {{0, 2, UP}}},
140 });
141 runEvents();
142}
143
144TEST_F(DebounceTest, TwoKeysSimultaneous1) {
145 addEvents({ /* Time, Inputs, Outputs */
146 {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},
147
148 {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
149 {6, {{0, 1, UP}, {0, 2, UP}}, {}},
150
151 {11, {}, {{0, 1, UP}, {0, 2, UP}}},
152 });
153 runEvents();
154}
155
156TEST_F(DebounceTest, TwoKeysSimultaneous2) {
157 addEvents({ /* Time, Inputs, Outputs */
158 {0, {{0, 1, DOWN}}, {}},
159 {1, {{0, 2, DOWN}}, {}},
160
161 {5, {}, {{0, 1, DOWN}}},
162 {6, {{0, 1, UP}}, {{0, 2, DOWN}}},
163 {7, {{0, 2, UP}}, {}},
164
165 {11, {}, {{0, 1, UP}}},
166 {12, {}, {{0, 2, UP}}},
167 });
168 runEvents();
169}
170
171TEST_F(DebounceTest, OneKeyDelayedScan1) {
172 addEvents({ /* Time, Inputs, Outputs */
173 {0, {{0, 1, DOWN}}, {}},
174
175 /* Processing is very late */
176 {300, {}, {{0, 1, DOWN}}},
177 /* Immediately release key */
178 {300, {{0, 1, UP}}, {}},
179
180 {305, {}, {{0, 1, UP}}},
181 });
182 time_jumps_ = true;
183 runEvents();
184}
185
186TEST_F(DebounceTest, OneKeyDelayedScan2) {
187 addEvents({ /* Time, Inputs, Outputs */
188 {0, {{0, 1, DOWN}}, {}},
189
190 /* Processing is very late */
191 {300, {}, {{0, 1, DOWN}}},
192 /* Release key after 1ms */
193 {301, {{0, 1, UP}}, {}},
194
195 {306, {}, {{0, 1, UP}}},
196 });
197 time_jumps_ = true;
198 runEvents();
199}
200
201TEST_F(DebounceTest, OneKeyDelayedScan3) {
202 addEvents({ /* Time, Inputs, Outputs */
203 {0, {{0, 1, DOWN}}, {}},
204
205 /* Release key before debounce expires */
206 {300, {{0, 1, UP}}, {}},
207 });
208 time_jumps_ = true;
209 runEvents();
210}
211
212TEST_F(DebounceTest, OneKeyDelayedScan4) {
213 addEvents({ /* Time, Inputs, Outputs */
214 {0, {{0, 1, DOWN}}, {}},
215
216 /* Processing is a bit late */
217 {50, {}, {{0, 1, DOWN}}},
218 /* Release key after 1ms */
219 {51, {{0, 1, UP}}, {}},
220
221 {56, {}, {{0, 1, UP}}},
222 });
223 time_jumps_ = true;
224 runEvents();
225}
diff --git a/quantum/debounce/tests/sym_eager_pk_tests.cpp b/quantum/debounce/tests/sym_eager_pk_tests.cpp
new file mode 100644
index 000000000..e0fc205e3
--- /dev/null
+++ b/quantum/debounce/tests/sym_eager_pk_tests.cpp
@@ -0,0 +1,237 @@
1/* Copyright 2021 Simon Arlott
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 "gtest/gtest.h"
18
19#include "debounce_test_common.h"
20
21TEST_F(DebounceTest, OneKeyShort1) {
22 addEvents({ /* Time, Inputs, Outputs */
23 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
24 {1, {{0, 1, UP}}, {}},
25
26 {5, {}, {{0, 1, UP}}},
27 /* Press key again after 1ms delay (debounce has not yet finished) */
28 {6, {{0, 1, DOWN}}, {}},
29 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
30 });
31 runEvents();
32}
33
34TEST_F(DebounceTest, OneKeyShort2) {
35 addEvents({ /* Time, Inputs, Outputs */
36 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
37 {1, {{0, 1, UP}}, {}},
38
39 {5, {}, {{0, 1, UP}}},
40 /* Press key again after 2ms delay (debounce has not yet finished) */
41 {7, {{0, 1, DOWN}}, {}},
42 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
43 });
44 runEvents();
45}
46
47TEST_F(DebounceTest, OneKeyShort3) {
48 addEvents({ /* Time, Inputs, Outputs */
49 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
50 {1, {{0, 1, UP}}, {}},
51
52 {5, {}, {{0, 1, UP}}},
53 /* Press key again after 3ms delay (debounce has not yet finished) */
54 {8, {{0, 1, DOWN}}, {}},
55 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
56 });
57 runEvents();
58}
59
60TEST_F(DebounceTest, OneKeyShort4) {
61 addEvents({ /* Time, Inputs, Outputs */
62 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
63 {1, {{0, 1, UP}}, {}},
64
65 {5, {}, {{0, 1, UP}}},
66 /* Press key again after 4ms delay (debounce has not yet finished) */
67 {9, {{0, 1, DOWN}}, {}},
68 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
69 });
70 runEvents();
71}
72
73TEST_F(DebounceTest, OneKeyShort5) {
74 addEvents({ /* Time, Inputs, Outputs */
75 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
76 {1, {{0, 1, UP}}, {}},
77
78 {5, {}, {{0, 1, UP}}},
79 /* Press key again after 5ms delay (debounce has finished) */
80 {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
81 });
82 runEvents();
83}
84
85TEST_F(DebounceTest, OneKeyShort6) {
86 addEvents({ /* Time, Inputs, Outputs */
87 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
88 {1, {{0, 1, UP}}, {}},
89
90 {5, {}, {{0, 1, UP}}},
91 /* Press key after after 6ms delay (debounce has finished) */
92 {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
93 });
94 runEvents();
95}
96
97TEST_F(DebounceTest, OneKeyBouncing1) {
98 addEvents({ /* Time, Inputs, Outputs */
99 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
100 {1, {{0, 1, UP}}, {}},
101 {2, {{0, 1, DOWN}}, {}},
102 {3, {{0, 1, UP}}, {}},
103 {4, {{0, 1, DOWN}}, {}},
104 {5, {{0, 1, UP}}, {{0, 1, UP}}},
105 /* Press key again after 1ms delay (debounce has not yet finished) */
106 {6, {{0, 1, DOWN}}, {}},
107 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
108 });
109 runEvents();
110}
111
112TEST_F(DebounceTest, OneKeyBouncing2) {
113 addEvents({ /* Time, Inputs, Outputs */
114 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
115 /* Change twice in the same time period */
116 {1, {{0, 1, UP}}, {}},
117 {1, {{0, 1, DOWN}}, {}},
118 /* Change three times in the same time period */
119 {2, {{0, 1, UP}}, {}},
120 {2, {{0, 1, DOWN}}, {}},
121 {2, {{0, 1, UP}}, {}},
122 /* Change three times in the same time period */
123 {3, {{0, 1, DOWN}}, {}},
124 {3, {{0, 1, UP}}, {}},
125 {3, {{0, 1, DOWN}}, {}},
126 /* Change twice in the same time period */
127 {4, {{0, 1, UP}}, {}},
128 {4, {{0, 1, DOWN}}, {}},
129 {5, {{0, 1, UP}}, {{0, 1, UP}}},
130 /* Press key again after 1ms delay (debounce has not yet finished) */
131 {6, {{0, 1, DOWN}}, {}},
132 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
133 });
134 runEvents();
135}
136
137TEST_F(DebounceTest, OneKeyLong) {
138 addEvents({ /* Time, Inputs, Outputs */
139 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
140
141 {25, {{0, 1, UP}}, {{0, 1, UP}}},
142
143 {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
144 });
145 runEvents();
146}
147
148TEST_F(DebounceTest, TwoKeysShort) {
149 addEvents({ /* Time, Inputs, Outputs */
150 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
151 {1, {{0, 1, UP}}, {}},
152 {2, {{0, 2, DOWN}}, {{0, 2, DOWN}}},
153 {3, {{0, 2, UP}}, {}},
154
155 {5, {}, {{0, 1, UP}}},
156 /* Press key again after 1ms delay (debounce has not yet finished) */
157 {6, {{0, 1, DOWN}}, {}},
158 {7, {}, {{0, 2, UP}}},
159
160 /* Press key again after 1ms delay (debounce has not yet finished) */
161 {9, {{0, 2, DOWN}}, {}},
162 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
163
164 {12, {}, {{0, 2, DOWN}}}, /* 5ms after UP at time 7 */
165 });
166 runEvents();
167}
168
169TEST_F(DebounceTest, OneKeyDelayedScan1) {
170 addEvents({ /* Time, Inputs, Outputs */
171 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
172
173 /* Processing is very late but the change will now be accepted */
174 {300, {{0, 1, UP}}, {{0, 1, UP}}},
175 });
176 time_jumps_ = true;
177 runEvents();
178}
179
180TEST_F(DebounceTest, OneKeyDelayedScan2) {
181 addEvents({ /* Time, Inputs, Outputs */
182 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
183
184 /* Processing is very late but the change will now be accepted even with a 1 scan delay */
185 {300, {}, {}},
186 {300, {{0, 1, UP}}, {{0, 1, UP}}},
187 });
188 time_jumps_ = true;
189 runEvents();
190}
191
192TEST_F(DebounceTest, OneKeyDelayedScan3) {
193 addEvents({ /* Time, Inputs, Outputs */
194 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
195
196 /* Processing is very late but the change will now be accepted even with a 1ms delay */
197 {300, {}, {}},
198 {301, {{0, 1, UP}}, {{0, 1, UP}}},
199 });
200 time_jumps_ = true;
201 runEvents();
202}
203
204TEST_F(DebounceTest, OneKeyDelayedScan4) {
205 addEvents({ /* Time, Inputs, Outputs */
206 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
207
208 /* Processing is a bit late but the change will now be accepted */
209 {50, {{0, 1, UP}}, {{0, 1, UP}}},
210 });
211 time_jumps_ = true;
212 runEvents();
213}
214
215TEST_F(DebounceTest, OneKeyDelayedScan5) {
216 addEvents({ /* Time, Inputs, Outputs */
217 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
218
219 /* Processing is very late but the change will now be accepted even with a 1 scan delay */
220 {50, {}, {}},
221 {50, {{0, 1, UP}}, {{0, 1, UP}}},
222 });
223 time_jumps_ = true;
224 runEvents();
225}
226
227TEST_F(DebounceTest, OneKeyDelayedScan6) {
228 addEvents({ /* Time, Inputs, Outputs */
229 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
230
231 /* Processing is very late but the change will now be accepted even with a 1ms delay */
232 {50, {}, {}},
233 {51, {{0, 1, UP}}, {{0, 1, UP}}},
234 });
235 time_jumps_ = true;
236 runEvents();
237}
diff --git a/quantum/debounce/tests/sym_eager_pr_tests.cpp b/quantum/debounce/tests/sym_eager_pr_tests.cpp
new file mode 100644
index 000000000..2c4bca127
--- /dev/null
+++ b/quantum/debounce/tests/sym_eager_pr_tests.cpp
@@ -0,0 +1,280 @@
1/* Copyright 2021 Simon Arlott
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 "gtest/gtest.h"
18
19#include "debounce_test_common.h"
20
21TEST_F(DebounceTest, OneKeyShort1) {
22 addEvents({ /* Time, Inputs, Outputs */
23 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
24 {1, {{0, 1, UP}}, {}},
25
26 {5, {}, {{0, 1, UP}}},
27 /* Press key again after 1ms delay (debounce has not yet finished) */
28 {6, {{0, 1, DOWN}}, {}},
29 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
30 });
31 runEvents();
32}
33
34TEST_F(DebounceTest, OneKeyShort2) {
35 addEvents({ /* Time, Inputs, Outputs */
36 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
37 {1, {{0, 1, UP}}, {}},
38
39 {5, {}, {{0, 1, UP}}},
40 /* Press key again after 2ms delay (debounce has not yet finished) */
41 {7, {{0, 1, DOWN}}, {}},
42 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
43 });
44 runEvents();
45}
46
47TEST_F(DebounceTest, OneKeyShort3) {
48 addEvents({ /* Time, Inputs, Outputs */
49 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
50 {1, {{0, 1, UP}}, {}},
51
52 {5, {}, {{0, 1, UP}}},
53 /* Press key again after 3ms delay (debounce has not yet finished) */
54 {8, {{0, 1, DOWN}}, {}},
55 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
56 });
57 runEvents();
58}
59
60TEST_F(DebounceTest, OneKeyShort4) {
61 addEvents({ /* Time, Inputs, Outputs */
62 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
63 {1, {{0, 1, UP}}, {}},
64
65 {5, {}, {{0, 1, UP}}},
66 /* Press key again after 4ms delay (debounce has not yet finished) */
67 {9, {{0, 1, DOWN}}, {}},
68 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
69 });
70 runEvents();
71}
72
73TEST_F(DebounceTest, OneKeyShort5) {
74 addEvents({ /* Time, Inputs, Outputs */
75 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
76 {1, {{0, 1, UP}}, {}},
77
78 {5, {}, {{0, 1, UP}}},
79 /* Press key again after 5ms delay (debounce has finished) */
80 {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
81 });
82 runEvents();
83}
84
85TEST_F(DebounceTest, OneKeyShort6) {
86 addEvents({ /* Time, Inputs, Outputs */
87 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
88 {1, {{0, 1, UP}}, {}},
89
90 {5, {}, {{0, 1, UP}}},
91 /* Press key after after 6ms delay (debounce has finished) */
92 {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
93 });
94 runEvents();
95}
96
97TEST_F(DebounceTest, OneKeyBouncing1) {
98 addEvents({ /* Time, Inputs, Outputs */
99 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
100 {1, {{0, 1, UP}}, {}},
101 {2, {{0, 1, DOWN}}, {}},
102 {3, {{0, 1, UP}}, {}},
103 {4, {{0, 1, DOWN}}, {}},
104 {5, {{0, 1, UP}}, {{0, 1, UP}}},
105 /* Press key again after 1ms delay (debounce has not yet finished) */
106 {6, {{0, 1, DOWN}}, {}},
107 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
108 });
109 runEvents();
110}
111
112TEST_F(DebounceTest, OneKeyBouncing2) {
113 addEvents({ /* Time, Inputs, Outputs */
114 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
115 /* Change twice in the same time period */
116 {1, {{0, 1, UP}}, {}},
117 {1, {{0, 1, DOWN}}, {}},
118 /* Change three times in the same time period */
119 {2, {{0, 1, UP}}, {}},
120 {2, {{0, 1, DOWN}}, {}},
121 {2, {{0, 1, UP}}, {}},
122 /* Change three times in the same time period */
123 {3, {{0, 1, DOWN}}, {}},
124 {3, {{0, 1, UP}}, {}},
125 {3, {{0, 1, DOWN}}, {}},
126 /* Change twice in the same time period */
127 {4, {{0, 1, UP}}, {}},
128 {4, {{0, 1, DOWN}}, {}},
129 {5, {{0, 1, UP}}, {{0, 1, UP}}},
130 /* Press key again after 1ms delay (debounce has not yet finished) */
131 {6, {{0, 1, DOWN}}, {}},
132 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
133 });
134 runEvents();
135}
136
137TEST_F(DebounceTest, OneKeyLong) {
138 addEvents({ /* Time, Inputs, Outputs */
139 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
140
141 {25, {{0, 1, UP}}, {{0, 1, UP}}},
142
143 {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
144 });
145 runEvents();
146}
147
148TEST_F(DebounceTest, TwoRowsShort) {
149 addEvents({ /* Time, Inputs, Outputs */
150 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
151 {1, {{0, 1, UP}}, {}},
152 {2, {{2, 0, DOWN}}, {{2, 0, DOWN}}},
153 {3, {{2, 0, UP}}, {}},
154
155 {5, {}, {{0, 1, UP}}},
156 /* Press key again after 1ms delay (debounce has not yet finished) */
157 {6, {{0, 1, DOWN}}, {}},
158 {7, {}, {{2, 0, UP}}},
159
160 /* Press key again after 1ms delay (debounce has not yet finished) */
161 {9, {{2, 0, DOWN}}, {}},
162 {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
163
164 {12, {}, {{2, 0, DOWN}}}, /* 5ms after UP at time 7 */
165 });
166 runEvents();
167}
168
169TEST_F(DebounceTest, TwoKeysOverlap) {
170 addEvents({ /* Time, Inputs, Outputs */
171 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
172 {1, {{0, 1, UP}}, {}},
173 /* Press a second key during the first debounce */
174 {2, {{0, 2, DOWN}}, {}},
175
176 /* Key registers as soon as debounce finishes, 5ms after time 0 */
177 {5, {}, {{0, 1, UP}, {0, 2, DOWN}}},
178 {6, {{0, 1, DOWN}}, {}},
179
180 /* Key registers as soon as debounce finishes, 5ms after time 5 */
181 {10, {}, {{0, 1, DOWN}}},
182 /* Release both keys */
183 {11, {{0, 1, UP}}, {}},
184 {12, {{0, 2, UP}}, {}},
185
186 /* Keys register as soon as debounce finishes, 5ms after time 10 */
187 {15, {}, {{0, 1, UP}, {0, 2, UP}}},
188 });
189 runEvents();
190}
191
192TEST_F(DebounceTest, TwoKeysSimultaneous1) {
193 addEvents({ /* Time, Inputs, Outputs */
194 {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}},
195 {20, {{0, 1, UP}}, {{0, 1, UP}}},
196 {21, {{0, 2, UP}}, {}},
197
198 /* Key registers as soon as debounce finishes, 5ms after time 20 */
199 {25, {}, {{0, 2, UP}}},
200 });
201 runEvents();
202}
203
204TEST_F(DebounceTest, TwoKeysSimultaneous2) {
205 addEvents({ /* Time, Inputs, Outputs */
206 {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}},
207 {20, {{0, 1, UP}, {0, 2, UP}}, {{0, 1, UP}, {0, 2, UP}}},
208 });
209 runEvents();
210}
211
212TEST_F(DebounceTest, OneKeyDelayedScan1) {
213 addEvents({ /* Time, Inputs, Outputs */
214 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
215
216 /* Processing is very late but the change will now be accepted */
217 {300, {{0, 1, UP}}, {{0, 1, UP}}},
218 });
219 time_jumps_ = true;
220 runEvents();
221}
222
223TEST_F(DebounceTest, OneKeyDelayedScan2) {
224 addEvents({ /* Time, Inputs, Outputs */
225 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
226
227 /* Processing is very late but the change will now be accepted even with a 1 scan delay */
228 {300, {}, {}},
229 {300, {{0, 1, UP}}, {{0, 1, UP}}},
230 });
231 time_jumps_ = true;
232 runEvents();
233}
234
235TEST_F(DebounceTest, OneKeyDelayedScan3) {
236 addEvents({ /* Time, Inputs, Outputs */
237 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
238
239 /* Processing is very late but the change will now be accepted even with a 1ms delay */
240 {300, {}, {}},
241 {301, {{0, 1, UP}}, {{0, 1, UP}}},
242 });
243 time_jumps_ = true;
244 runEvents();
245}
246
247TEST_F(DebounceTest, OneKeyDelayedScan4) {
248 addEvents({ /* Time, Inputs, Outputs */
249 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
250
251 /* Processing is a bit late but the change will now be accepted */
252 {50, {{0, 1, UP}}, {{0, 1, UP}}},
253 });
254 time_jumps_ = true;
255 runEvents();
256}
257
258TEST_F(DebounceTest, OneKeyDelayedScan5) {
259 addEvents({ /* Time, Inputs, Outputs */
260 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
261
262 /* Processing is very late but the change will now be accepted even with a 1 scan delay */
263 {50, {}, {}},
264 {50, {{0, 1, UP}}, {{0, 1, UP}}},
265 });
266 time_jumps_ = true;
267 runEvents();
268}
269
270TEST_F(DebounceTest, OneKeyDelayedScan6) {
271 addEvents({ /* Time, Inputs, Outputs */
272 {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
273
274 /* Processing is very late but the change will now be accepted even with a 1ms delay */
275 {50, {}, {}},
276 {51, {{0, 1, UP}}, {{0, 1, UP}}},
277 });
278 time_jumps_ = true;
279 runEvents();
280}
diff --git a/quantum/debounce/tests/testlist.mk b/quantum/debounce/tests/testlist.mk
new file mode 100644
index 000000000..16ce8a0a8
--- /dev/null
+++ b/quantum/debounce/tests/testlist.mk
@@ -0,0 +1,5 @@
1TEST_LIST += \
2 debounce_sym_defer_g \
3 debounce_sym_defer_pk \
4 debounce_sym_eager_pk \
5 debounce_sym_eager_pr
diff --git a/quantum/led_matrix.c b/quantum/led_matrix.c
index 7e0fdf896..942e6d7dc 100644
--- a/quantum/led_matrix.c
+++ b/quantum/led_matrix.c
@@ -67,10 +67,6 @@ const led_point_t k_led_matrix_center = LED_MATRIX_CENTER;
67# define LED_DISABLE_TIMEOUT 0 67# define LED_DISABLE_TIMEOUT 0
68#endif 68#endif
69 69
70#if LED_DISABLE_WHEN_USB_SUSPENDED != 1
71# undef LED_DISABLE_WHEN_USB_SUSPENDED
72#endif
73
74#if !defined(LED_MATRIX_MAXIMUM_BRIGHTNESS) || LED_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX 70#if !defined(LED_MATRIX_MAXIMUM_BRIGHTNESS) || LED_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX
75# undef LED_MATRIX_MAXIMUM_BRIGHTNESS 71# undef LED_MATRIX_MAXIMUM_BRIGHTNESS
76# define LED_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX 72# define LED_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX
diff --git a/quantum/matrix.c b/quantum/matrix.c
index 34d6af2e6..71ef27089 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -16,6 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/ 16*/
17#include <stdint.h> 17#include <stdint.h>
18#include <stdbool.h> 18#include <stdbool.h>
19#include <string.h>
19#include "util.h" 20#include "util.h"
20#include "matrix.h" 21#include "matrix.h"
21#include "debounce.h" 22#include "debounce.h"
@@ -24,14 +25,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
24#ifdef DIRECT_PINS 25#ifdef DIRECT_PINS
25static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS; 26static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS;
26#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW) 27#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW)
28# ifdef MATRIX_ROW_PINS
27static const pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; 29static const pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
30# endif // MATRIX_ROW_PINS
31# ifdef MATRIX_COL_PINS
28static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; 32static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
33# endif // MATRIX_COL_PINS
29#endif 34#endif
30 35
31/* matrix state(1:on, 0:off) */ 36/* matrix state(1:on, 0:off) */
32extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values 37extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
33extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values 38extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values
34 39
40// user-defined overridable functions
41__attribute__((weak)) void matrix_init_pins(void);
42__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
43__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
44
35static inline void setPinOutput_writeLow(pin_t pin) { 45static inline void setPinOutput_writeLow(pin_t pin) {
36 ATOMIC_BLOCK_FORCEON { 46 ATOMIC_BLOCK_FORCEON {
37 setPinOutput(pin); 47 setPinOutput(pin);
@@ -47,7 +57,7 @@ static inline void setPinInputHigh_atomic(pin_t pin) {
47 57
48#ifdef DIRECT_PINS 58#ifdef DIRECT_PINS
49 59
50static void init_pins(void) { 60__attribute__((weak)) void matrix_init_pins(void) {
51 for (int row = 0; row < MATRIX_ROWS; row++) { 61 for (int row = 0; row < MATRIX_ROWS; row++) {
52 for (int col = 0; col < MATRIX_COLS; col++) { 62 for (int col = 0; col < MATRIX_COLS; col++) {
53 pin_t pin = direct_pins[row][col]; 63 pin_t pin = direct_pins[row][col];
@@ -58,7 +68,7 @@ static void init_pins(void) {
58 } 68 }
59} 69}
60 70
61static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { 71__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
62 // Start with a clear matrix row 72 // Start with a clear matrix row
63 matrix_row_t current_row_value = 0; 73 matrix_row_t current_row_value = 0;
64 74
@@ -69,16 +79,13 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
69 } 79 }
70 } 80 }
71 81
72 // If the row has changed, store the row and return the changed flag. 82 // Update the matrix
73 if (current_matrix[current_row] != current_row_value) { 83 current_matrix[current_row] = current_row_value;
74 current_matrix[current_row] = current_row_value;
75 return true;
76 }
77 return false;
78} 84}
79 85
80#elif defined(DIODE_DIRECTION) 86#elif defined(DIODE_DIRECTION)
81# if (DIODE_DIRECTION == COL2ROW) 87# if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)
88# if (DIODE_DIRECTION == COL2ROW)
82 89
83static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); } 90static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); }
84 91
@@ -90,14 +97,14 @@ static void unselect_rows(void) {
90 } 97 }
91} 98}
92 99
93static void init_pins(void) { 100__attribute__((weak)) void matrix_init_pins(void) {
94 unselect_rows(); 101 unselect_rows();
95 for (uint8_t x = 0; x < MATRIX_COLS; x++) { 102 for (uint8_t x = 0; x < MATRIX_COLS; x++) {
96 setPinInputHigh_atomic(col_pins[x]); 103 setPinInputHigh_atomic(col_pins[x]);
97 } 104 }
98} 105}
99 106
100static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { 107__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
101 // Start with a clear matrix row 108 // Start with a clear matrix row
102 matrix_row_t current_row_value = 0; 109 matrix_row_t current_row_value = 0;
103 110
@@ -118,15 +125,11 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
118 unselect_row(current_row); 125 unselect_row(current_row);
119 matrix_output_unselect_delay(); // wait for all Col signals to go HIGH 126 matrix_output_unselect_delay(); // wait for all Col signals to go HIGH
120 127
121 // If the row has changed, store the row and return the changed flag. 128 // Update the matrix
122 if (current_matrix[current_row] != current_row_value) { 129 current_matrix[current_row] = current_row_value;
123 current_matrix[current_row] = current_row_value;
124 return true;
125 }
126 return false;
127} 130}
128 131
129# elif (DIODE_DIRECTION == ROW2COL) 132# elif (DIODE_DIRECTION == ROW2COL)
130 133
131static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); } 134static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); }
132 135
@@ -138,59 +141,46 @@ static void unselect_cols(void) {
138 } 141 }
139} 142}
140 143
141static void init_pins(void) { 144__attribute__((weak)) void matrix_init_pins(void) {
142 unselect_cols(); 145 unselect_cols();
143 for (uint8_t x = 0; x < MATRIX_ROWS; x++) { 146 for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
144 setPinInputHigh_atomic(row_pins[x]); 147 setPinInputHigh_atomic(row_pins[x]);
145 } 148 }
146} 149}
147 150
148static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { 151__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
149 bool matrix_changed = false;
150
151 // Select col 152 // Select col
152 select_col(current_col); 153 select_col(current_col);
153 matrix_output_select_delay(); 154 matrix_output_select_delay();
154 155
155 // For each row... 156 // For each row...
156 for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) { 157 for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) {
157 // Store last value of row prior to reading
158 matrix_row_t last_row_value = current_matrix[row_index];
159 matrix_row_t current_row_value = last_row_value;
160
161 // Check row pin state 158 // Check row pin state
162 if (readPin(row_pins[row_index]) == 0) { 159 if (readPin(row_pins[row_index]) == 0) {
163 // Pin LO, set col bit 160 // Pin LO, set col bit
164 current_row_value |= (MATRIX_ROW_SHIFTER << current_col); 161 current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col);
165 } else { 162 } else {
166 // Pin HI, clear col bit 163 // Pin HI, clear col bit
167 current_row_value &= ~(MATRIX_ROW_SHIFTER << current_col); 164 current_matrix[row_index] &= ~(MATRIX_ROW_SHIFTER << current_col);
168 }
169
170 // Determine if the matrix changed state
171 if ((last_row_value != current_row_value)) {
172 matrix_changed |= true;
173 current_matrix[row_index] = current_row_value;
174 } 165 }
175 } 166 }
176 167
177 // Unselect col 168 // Unselect col
178 unselect_col(current_col); 169 unselect_col(current_col);
179 matrix_output_unselect_delay(); // wait for all Row signals to go HIGH 170 matrix_output_unselect_delay(); // wait for all Row signals to go HIGH
180
181 return matrix_changed;
182} 171}
183 172
184# else 173# else
185# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL! 174# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL!
186# endif 175# endif
176# endif // defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)
187#else 177#else
188# error DIODE_DIRECTION is not defined! 178# error DIODE_DIRECTION is not defined!
189#endif 179#endif
190 180
191void matrix_init(void) { 181void matrix_init(void) {
192 // initialize key pins 182 // initialize key pins
193 init_pins(); 183 matrix_init_pins();
194 184
195 // initialize matrix state: all keys off 185 // initialize matrix state: all keys off
196 for (uint8_t i = 0; i < MATRIX_ROWS; i++) { 186 for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
@@ -204,20 +194,23 @@ void matrix_init(void) {
204} 194}
205 195
206uint8_t matrix_scan(void) { 196uint8_t matrix_scan(void) {
207 bool changed = false; 197 matrix_row_t curr_matrix[MATRIX_ROWS] = {0};
208 198
209#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) 199#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)
210 // Set row, read cols 200 // Set row, read cols
211 for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) { 201 for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
212 changed |= read_cols_on_row(raw_matrix, current_row); 202 matrix_read_cols_on_row(curr_matrix, current_row);
213 } 203 }
214#elif (DIODE_DIRECTION == ROW2COL) 204#elif (DIODE_DIRECTION == ROW2COL)
215 // Set col, read rows 205 // Set col, read rows
216 for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { 206 for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
217 changed |= read_rows_on_col(raw_matrix, current_col); 207 matrix_read_rows_on_col(curr_matrix, current_col);
218 } 208 }
219#endif 209#endif
220 210
211 bool changed = memcmp(raw_matrix, curr_matrix, sizeof(curr_matrix)) != 0;
212 if (changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix));
213
221 debounce(raw_matrix, matrix, MATRIX_ROWS, changed); 214 debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
222 215
223 matrix_scan_quantum(); 216 matrix_scan_quantum();
diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c
index ab8dbd849..27f641797 100644
--- a/quantum/rgb_matrix.c
+++ b/quantum/rgb_matrix.c
@@ -67,10 +67,6 @@ __attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv
67# define RGB_DISABLE_TIMEOUT 0 67# define RGB_DISABLE_TIMEOUT 0
68#endif 68#endif
69 69
70#if RGB_DISABLE_WHEN_USB_SUSPENDED != 1
71# undef RGB_DISABLE_WHEN_USB_SUSPENDED
72#endif
73
74#if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX 70#if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX
75# undef RGB_MATRIX_MAXIMUM_BRIGHTNESS 71# undef RGB_MATRIX_MAXIMUM_BRIGHTNESS
76# define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX 72# define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX
diff --git a/quantum/rgb_matrix.h b/quantum/rgb_matrix.h
index a615b8422..741a2fe44 100644
--- a/quantum/rgb_matrix.h
+++ b/quantum/rgb_matrix.h
@@ -33,6 +33,8 @@
33# include "is31fl3737.h" 33# include "is31fl3737.h"
34#elif defined(IS31FL3741) 34#elif defined(IS31FL3741)
35# include "is31fl3741.h" 35# include "is31fl3741.h"
36#elif defined(AW20216)
37# include "aw20216.h"
36#elif defined(WS2812) 38#elif defined(WS2812)
37# include "ws2812.h" 39# include "ws2812.h"
38#endif 40#endif
diff --git a/quantum/rgb_matrix_drivers.c b/quantum/rgb_matrix_drivers.c
index 896fa6d0e..6a11d4791 100644
--- a/quantum/rgb_matrix_drivers.c
+++ b/quantum/rgb_matrix_drivers.c
@@ -171,6 +171,22 @@ const rgb_matrix_driver_t rgb_matrix_driver = {
171}; 171};
172# endif 172# endif
173 173
174#elif defined(AW20216)
175# include "spi_master.h"
176static void init(void) {
177 spi_init();
178 AW20216_init();
179}
180
181static void flush(void) { AW20216_update_pwm_buffers(); }
182
183const rgb_matrix_driver_t rgb_matrix_driver = {
184 .init = init,
185 .flush = flush,
186 .set_color = AW20216_set_color,
187 .set_color_all = AW20216_set_color_all,
188};
189
174#elif defined(WS2812) 190#elif defined(WS2812)
175# if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_CUSTOM_DRIVER) 191# if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_CUSTOM_DRIVER)
176# pragma message "Cannot use RGBLIGHT and RGB Matrix using WS2812 at the same time." 192# pragma message "Cannot use RGBLIGHT and RGB Matrix using WS2812 at the same time."
diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c
index 039e7d977..2cf7b7058 100644
--- a/quantum/split_common/matrix.c
+++ b/quantum/split_common/matrix.c
@@ -16,6 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/ 16*/
17#include <stdint.h> 17#include <stdint.h>
18#include <stdbool.h> 18#include <stdbool.h>
19#include <string.h>
19#include "util.h" 20#include "util.h"
20#include "matrix.h" 21#include "matrix.h"
21#include "debounce.h" 22#include "debounce.h"
@@ -31,8 +32,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
31#ifdef DIRECT_PINS 32#ifdef DIRECT_PINS
32static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS; 33static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS;
33#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW) 34#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW)
35# ifdef MATRIX_ROW_PINS
34static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; 36static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
37# endif // MATRIX_ROW_PINS
38# ifdef MATRIX_COL_PINS
35static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; 39static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
40# endif // MATRIX_COL_PINS
36#endif 41#endif
37 42
38/* matrix state(1:on, 0:off) */ 43/* matrix state(1:on, 0:off) */
@@ -45,6 +50,9 @@ uint8_t thisHand, thatHand;
45// user-defined overridable functions 50// user-defined overridable functions
46__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); } 51__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); }
47__attribute__((weak)) void matrix_slave_scan_user(void) {} 52__attribute__((weak)) void matrix_slave_scan_user(void) {}
53__attribute__((weak)) void matrix_init_pins(void);
54__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
55__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
48 56
49static inline void setPinOutput_writeLow(pin_t pin) { 57static inline void setPinOutput_writeLow(pin_t pin) {
50 ATOMIC_BLOCK_FORCEON { 58 ATOMIC_BLOCK_FORCEON {
@@ -61,7 +69,7 @@ static inline void setPinInputHigh_atomic(pin_t pin) {
61 69
62#ifdef DIRECT_PINS 70#ifdef DIRECT_PINS
63 71
64static void init_pins(void) { 72__attribute__((weak)) void matrix_init_pins(void) {
65 for (int row = 0; row < MATRIX_ROWS; row++) { 73 for (int row = 0; row < MATRIX_ROWS; row++) {
66 for (int col = 0; col < MATRIX_COLS; col++) { 74 for (int col = 0; col < MATRIX_COLS; col++) {
67 pin_t pin = direct_pins[row][col]; 75 pin_t pin = direct_pins[row][col];
@@ -72,7 +80,7 @@ static void init_pins(void) {
72 } 80 }
73} 81}
74 82
75static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { 83__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
76 // Start with a clear matrix row 84 // Start with a clear matrix row
77 matrix_row_t current_row_value = 0; 85 matrix_row_t current_row_value = 0;
78 86
@@ -83,16 +91,13 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
83 } 91 }
84 } 92 }
85 93
86 // If the row has changed, store the row and return the changed flag. 94 // Update the matrix
87 if (current_matrix[current_row] != current_row_value) { 95 current_matrix[current_row] = current_row_value;
88 current_matrix[current_row] = current_row_value;
89 return true;
90 }
91 return false;
92} 96}
93 97
94#elif defined(DIODE_DIRECTION) 98#elif defined(DIODE_DIRECTION)
95# if (DIODE_DIRECTION == COL2ROW) 99# if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)
100# if (DIODE_DIRECTION == COL2ROW)
96 101
97static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); } 102static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); }
98 103
@@ -104,14 +109,14 @@ static void unselect_rows(void) {
104 } 109 }
105} 110}
106 111
107static void init_pins(void) { 112__attribute__((weak)) void matrix_init_pins(void) {
108 unselect_rows(); 113 unselect_rows();
109 for (uint8_t x = 0; x < MATRIX_COLS; x++) { 114 for (uint8_t x = 0; x < MATRIX_COLS; x++) {
110 setPinInputHigh_atomic(col_pins[x]); 115 setPinInputHigh_atomic(col_pins[x]);
111 } 116 }
112} 117}
113 118
114static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { 119__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
115 // Start with a clear matrix row 120 // Start with a clear matrix row
116 matrix_row_t current_row_value = 0; 121 matrix_row_t current_row_value = 0;
117 122
@@ -132,15 +137,11 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
132 unselect_row(current_row); 137 unselect_row(current_row);
133 matrix_output_unselect_delay(); // wait for all Col signals to go HIGH 138 matrix_output_unselect_delay(); // wait for all Col signals to go HIGH
134 139
135 // If the row has changed, store the row and return the changed flag. 140 // Update the matrix
136 if (current_matrix[current_row] != current_row_value) { 141 current_matrix[current_row] = current_row_value;
137 current_matrix[current_row] = current_row_value;
138 return true;
139 }
140 return false;
141} 142}
142 143
143# elif (DIODE_DIRECTION == ROW2COL) 144# elif (DIODE_DIRECTION == ROW2COL)
144 145
145static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); } 146static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); }
146 147
@@ -152,52 +153,39 @@ static void unselect_cols(void) {
152 } 153 }
153} 154}
154 155
155static void init_pins(void) { 156__attribute__((weak)) void matrix_init_pins(void) {
156 unselect_cols(); 157 unselect_cols();
157 for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { 158 for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
158 setPinInputHigh_atomic(row_pins[x]); 159 setPinInputHigh_atomic(row_pins[x]);
159 } 160 }
160} 161}
161 162
162static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { 163__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
163 bool matrix_changed = false;
164
165 // Select col 164 // Select col
166 select_col(current_col); 165 select_col(current_col);
167 matrix_output_select_delay(); 166 matrix_output_select_delay();
168 167
169 // For each row... 168 // For each row...
170 for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) { 169 for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) {
171 // Store last value of row prior to reading
172 matrix_row_t last_row_value = current_matrix[row_index];
173 matrix_row_t current_row_value = last_row_value;
174
175 // Check row pin state 170 // Check row pin state
176 if (readPin(row_pins[row_index]) == 0) { 171 if (readPin(row_pins[row_index]) == 0) {
177 // Pin LO, set col bit 172 // Pin LO, set col bit
178 current_row_value |= (MATRIX_ROW_SHIFTER << current_col); 173 current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col);
179 } else { 174 } else {
180 // Pin HI, clear col bit 175 // Pin HI, clear col bit
181 current_row_value &= ~(MATRIX_ROW_SHIFTER << current_col); 176 current_matrix[row_index] &= ~(MATRIX_ROW_SHIFTER << current_col);
182 }
183
184 // Determine if the matrix changed state
185 if ((last_row_value != current_row_value)) {
186 matrix_changed |= true;
187 current_matrix[row_index] = current_row_value;
188 } 177 }
189 } 178 }
190 179
191 // Unselect col 180 // Unselect col
192 unselect_col(current_col); 181 unselect_col(current_col);
193 matrix_output_unselect_delay(); // wait for all Row signals to go HIGH 182 matrix_output_unselect_delay(); // wait for all Row signals to go HIGH
194
195 return matrix_changed;
196} 183}
197 184
198# else 185# else
199# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL! 186# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL!
200# endif 187# endif
188# endif // defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)
201#else 189#else
202# error DIODE_DIRECTION is not defined! 190# error DIODE_DIRECTION is not defined!
203#endif 191#endif
@@ -233,7 +221,7 @@ void matrix_init(void) {
233 thatHand = ROWS_PER_HAND - thisHand; 221 thatHand = ROWS_PER_HAND - thisHand;
234 222
235 // initialize key pins 223 // initialize key pins
236 init_pins(); 224 matrix_init_pins();
237 225
238 // initialize matrix state: all keys off 226 // initialize matrix state: all keys off
239 for (uint8_t i = 0; i < MATRIX_ROWS; i++) { 227 for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
@@ -288,20 +276,23 @@ bool matrix_post_scan(void) {
288} 276}
289 277
290uint8_t matrix_scan(void) { 278uint8_t matrix_scan(void) {
291 bool local_changed = false; 279 matrix_row_t curr_matrix[MATRIX_ROWS] = {0};
292 280
293#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) 281#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)
294 // Set row, read cols 282 // Set row, read cols
295 for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) { 283 for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
296 local_changed |= read_cols_on_row(raw_matrix, current_row); 284 matrix_read_cols_on_row(curr_matrix, current_row);
297 } 285 }
298#elif (DIODE_DIRECTION == ROW2COL) 286#elif (DIODE_DIRECTION == ROW2COL)
299 // Set col, read rows 287 // Set col, read rows
300 for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { 288 for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
301 local_changed |= read_rows_on_col(raw_matrix, current_col); 289 matrix_read_rows_on_col(curr_matrix, current_col);
302 } 290 }
303#endif 291#endif
304 292
293 bool local_changed = memcmp(raw_matrix, curr_matrix, sizeof(curr_matrix)) != 0;
294 if (local_changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix));
295
305 debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed); 296 debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed);
306 297
307 bool remote_changed = matrix_post_scan(); 298 bool remote_changed = matrix_post_scan();