diff options
Diffstat (limited to 'quantum')
32 files changed, 3349 insertions, 630 deletions
diff --git a/quantum/crc.c b/quantum/crc.c new file mode 100644 index 000000000..0d8b9d601 --- /dev/null +++ b/quantum/crc.c | |||
@@ -0,0 +1,59 @@ | |||
1 | /* Copyright 2021 QMK | ||
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 "crc.h" | ||
18 | |||
19 | __attribute__((weak)) void crc_init(void){ | ||
20 | /* Software implementation nothing todo here. */ | ||
21 | }; | ||
22 | |||
23 | #if defined(CRC8_USE_TABLE) | ||
24 | /** | ||
25 | * Static table used for the table_driven implementation. | ||
26 | */ | ||
27 | static const crc_t crc_table[256] = {0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, | ||
28 | 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3}; | ||
29 | |||
30 | __attribute__((weak)) uint8_t crc8(const void *data, size_t data_len) { | ||
31 | const uint8_t *d = (const uint8_t *)data; | ||
32 | crc_t crc = 0xff; | ||
33 | size_t tbl_idx; | ||
34 | |||
35 | while (data_len--) { | ||
36 | tbl_idx = crc ^ *d; | ||
37 | crc = crc_table[tbl_idx] & 0xff; | ||
38 | d++; | ||
39 | } | ||
40 | return crc & 0xff; | ||
41 | } | ||
42 | #else | ||
43 | __attribute__((weak)) uint8_t crc8(const void *data, size_t data_len) { | ||
44 | const uint8_t *d = (const uint8_t *)data; | ||
45 | crc_t crc = 0xff; | ||
46 | size_t i, j; | ||
47 | |||
48 | for (i = 0; i < data_len; i++) { | ||
49 | crc ^= d[i]; | ||
50 | for (j = 0; j < 8; j++) { | ||
51 | if ((crc & 0x80) != 0) | ||
52 | crc = (crc_t)((crc << 1) ^ 0x31); | ||
53 | else | ||
54 | crc <<= 1; | ||
55 | } | ||
56 | } | ||
57 | return crc; | ||
58 | } | ||
59 | #endif \ No newline at end of file | ||
diff --git a/quantum/crc.h b/quantum/crc.h new file mode 100644 index 000000000..c17f5888e --- /dev/null +++ b/quantum/crc.h | |||
@@ -0,0 +1,44 @@ | |||
1 | /* Copyright 2021 QMK | ||
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 | #pragma once | ||
18 | |||
19 | #include "quantum.h" | ||
20 | |||
21 | /** | ||
22 | * The type of the CRC values. | ||
23 | * | ||
24 | * This type must be big enough to contain at least 8 bits. | ||
25 | */ | ||
26 | #if defined(CRC8_OPTIMIZE_SPEED) | ||
27 | typedef uint_fast8_t crc_t; | ||
28 | #else | ||
29 | typedef uint_least8_t crc_t; | ||
30 | #endif | ||
31 | |||
32 | /** | ||
33 | * Initialize crc subsystem. | ||
34 | */ | ||
35 | __attribute__((weak)) void crc_init(void); | ||
36 | |||
37 | /** | ||
38 | * Generate CRC8 value from given data. | ||
39 | * | ||
40 | * \param[in] data Pointer to a buffer of \a data_len bytes. | ||
41 | * \param[in] data_len Number of bytes in the \a data buffer. | ||
42 | * \return The calculated crc value. | ||
43 | */ | ||
44 | __attribute__((weak)) uint8_t crc8(const void *data, size_t data_len); \ No newline at end of file | ||
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 | |||
9 | bool debounce_active(void); | 9 | bool debounce_active(void); |
10 | 10 | ||
11 | void debounce_init(uint8_t num_rows); | 11 | void debounce_init(uint8_t num_rows); |
12 | |||
13 | void debounce_free(void); | ||
diff --git a/quantum/debounce/asym_eager_defer_pk.c b/quantum/debounce/asym_eager_defer_pk.c new file mode 100644 index 000000000..24380dc5e --- /dev/null +++ b/quantum/debounce/asym_eager_defer_pk.c | |||
@@ -0,0 +1,171 @@ | |||
1 | /* | ||
2 | * Copyright 2017 Alex Ong <the.onga@gmail.com> | ||
3 | * Copyright 2020 Andrei Purdea <andrei@purdea.ro> | ||
4 | * Copyright 2021 Simon Arlott | ||
5 | * | ||
6 | * This program is free software: you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation, either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | /* | ||
21 | Basic symmetric per-key algorithm. Uses an 8-bit counter per key. | ||
22 | When no state changes have occured for DEBOUNCE milliseconds, we push the state. | ||
23 | */ | ||
24 | |||
25 | #include "matrix.h" | ||
26 | #include "timer.h" | ||
27 | #include "quantum.h" | ||
28 | #include <stdlib.h> | ||
29 | |||
30 | #ifdef PROTOCOL_CHIBIOS | ||
31 | # if CH_CFG_USE_MEMCORE == FALSE | ||
32 | # error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm. | ||
33 | # endif | ||
34 | #endif | ||
35 | |||
36 | #ifndef DEBOUNCE | ||
37 | # define DEBOUNCE 5 | ||
38 | #endif | ||
39 | |||
40 | // Maximum debounce: 127ms | ||
41 | #if DEBOUNCE > 127 | ||
42 | # undef DEBOUNCE | ||
43 | # define DEBOUNCE 127 | ||
44 | #endif | ||
45 | |||
46 | #define ROW_SHIFTER ((matrix_row_t)1) | ||
47 | |||
48 | typedef struct { | ||
49 | bool pressed : 1; | ||
50 | uint8_t time : 7; | ||
51 | } debounce_counter_t; | ||
52 | |||
53 | #if DEBOUNCE > 0 | ||
54 | static debounce_counter_t *debounce_counters; | ||
55 | static fast_timer_t last_time; | ||
56 | static bool counters_need_update; | ||
57 | static bool matrix_need_update; | ||
58 | |||
59 | #define DEBOUNCE_ELAPSED 0 | ||
60 | |||
61 | static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time); | ||
62 | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); | ||
63 | |||
64 | // we use num_rows rather than MATRIX_ROWS to support split keyboards | ||
65 | void debounce_init(uint8_t num_rows) { | ||
66 | debounce_counters = malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t)); | ||
67 | int i = 0; | ||
68 | for (uint8_t r = 0; r < num_rows; r++) { | ||
69 | for (uint8_t c = 0; c < MATRIX_COLS; c++) { | ||
70 | debounce_counters[i++].time = DEBOUNCE_ELAPSED; | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | void debounce_free(void) { | ||
76 | free(debounce_counters); | ||
77 | debounce_counters = NULL; | ||
78 | } | ||
79 | |||
80 | void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | ||
81 | bool updated_last = false; | ||
82 | |||
83 | if (counters_need_update) { | ||
84 | fast_timer_t now = timer_read_fast(); | ||
85 | fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); | ||
86 | |||
87 | last_time = now; | ||
88 | updated_last = true; | ||
89 | if (elapsed_time > UINT8_MAX) { | ||
90 | elapsed_time = UINT8_MAX; | ||
91 | } | ||
92 | |||
93 | if (elapsed_time > 0) { | ||
94 | update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | if (changed || matrix_need_update) { | ||
99 | if (!updated_last) { | ||
100 | last_time = timer_read_fast(); | ||
101 | } | ||
102 | |||
103 | transfer_matrix_values(raw, cooked, num_rows); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) { | ||
108 | debounce_counter_t *debounce_pointer = debounce_counters; | ||
109 | |||
110 | counters_need_update = false; | ||
111 | matrix_need_update = false; | ||
112 | |||
113 | for (uint8_t row = 0; row < num_rows; row++) { | ||
114 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { | ||
115 | matrix_row_t col_mask = (ROW_SHIFTER << col); | ||
116 | |||
117 | if (debounce_pointer->time != DEBOUNCE_ELAPSED) { | ||
118 | if (debounce_pointer->time <= elapsed_time) { | ||
119 | debounce_pointer->time = DEBOUNCE_ELAPSED; | ||
120 | |||
121 | if (debounce_pointer->pressed) { | ||
122 | // key-down: eager | ||
123 | matrix_need_update = true; | ||
124 | } else { | ||
125 | // key-up: defer | ||
126 | cooked[row] = (cooked[row] & ~col_mask) | (raw[row] & col_mask); | ||
127 | } | ||
128 | } else { | ||
129 | debounce_pointer->time -= elapsed_time; | ||
130 | counters_need_update = true; | ||
131 | } | ||
132 | } | ||
133 | debounce_pointer++; | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { | ||
139 | debounce_counter_t *debounce_pointer = debounce_counters; | ||
140 | |||
141 | for (uint8_t row = 0; row < num_rows; row++) { | ||
142 | matrix_row_t delta = raw[row] ^ cooked[row]; | ||
143 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { | ||
144 | matrix_row_t col_mask = (ROW_SHIFTER << col); | ||
145 | |||
146 | if (delta & col_mask) { | ||
147 | if (debounce_pointer->time == DEBOUNCE_ELAPSED) { | ||
148 | debounce_pointer->pressed = (raw[row] & col_mask); | ||
149 | debounce_pointer->time = DEBOUNCE; | ||
150 | counters_need_update = true; | ||
151 | |||
152 | if (debounce_pointer->pressed) { | ||
153 | // key-down: eager | ||
154 | cooked[row] ^= col_mask; | ||
155 | } | ||
156 | } | ||
157 | } else if (debounce_pointer->time != DEBOUNCE_ELAPSED) { | ||
158 | if (!debounce_pointer->pressed) { | ||
159 | // key-up: defer | ||
160 | debounce_pointer->time = DEBOUNCE_ELAPSED; | ||
161 | } | ||
162 | } | ||
163 | debounce_pointer++; | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | |||
168 | bool debounce_active(void) { return true; } | ||
169 | #else | ||
170 | # include "none.c" | ||
171 | #endif | ||
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 | |||
21 | void debounce_init(uint8_t num_rows) {} | ||
22 | |||
23 | void 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 | |||
29 | bool debounce_active(void) { return false; } | ||
30 | |||
31 | void 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 | /* |
2 | Copyright 2017 Alex Ong<the.onga@gmail.com> | 2 | Copyright 2017 Alex Ong<the.onga@gmail.com> |
3 | Copyright 2021 Simon Arlott | ||
3 | This program is free software: you can redistribute it and/or modify | 4 | 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 | 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 | the 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 | ||
26 | void debounce_init(uint8_t num_rows) {} | 27 | #if DEBOUNCE > 0 |
27 | static bool debouncing = false; | 28 | static bool debouncing = false; |
29 | static fast_timer_t debouncing_time; | ||
28 | 30 | ||
29 | #if DEBOUNCE > 0 | 31 | void debounce_init(uint8_t num_rows) {} |
30 | static uint16_t debouncing_time; | 32 | |
31 | void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 33 | void 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. | ||
45 | void 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 | ||
52 | bool debounce_active(void) { return debouncing; } | 47 | bool debounce_active(void) { return debouncing; } |
48 | |||
49 | void 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 | /* |
2 | Copyright 2017 Alex Ong<the.onga@gmail.com> | 2 | Copyright 2017 Alex Ong<the.onga@gmail.com> |
3 | Copyright 2020 Andrei Purdea<andrei@purdea.ro> | 3 | Copyright 2020 Andrei Purdea<andrei@purdea.ro> |
4 | Copyright 2021 Simon Arlott | ||
4 | This program is free software: you can redistribute it and/or modify | 5 | This program is free software: you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by | 6 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation, either version 2 of the License, or | 7 | the 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 | 45 | typedef uint8_t debounce_counter_t; |
39 | 46 | ||
47 | #if DEBOUNCE > 0 | ||
40 | static debounce_counter_t *debounce_counters; | 48 | static debounce_counter_t *debounce_counters; |
49 | static fast_timer_t last_time; | ||
41 | static bool counters_need_update; | 50 | static bool counters_need_update; |
42 | 51 | ||
43 | #define DEBOUNCE_ELAPSED 251 | 52 | #define DEBOUNCE_ELAPSED 0 |
44 | #define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) | ||
45 | |||
46 | static 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 | ||
56 | void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); | 54 | static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time); |
57 | void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); | 55 | static 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 |
60 | void debounce_init(uint8_t num_rows) { | 58 | void debounce_init(uint8_t num_rows) { |
@@ -67,27 +65,49 @@ void debounce_init(uint8_t num_rows) { | |||
67 | } | 65 | } |
68 | } | 66 | } |
69 | 67 | ||
68 | void debounce_free(void) { | ||
69 | free(debounce_counters); | ||
70 | debounce_counters = NULL; | ||
71 | } | ||
72 | |||
70 | void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 73 | void 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 | ||
81 | void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { | 100 | static 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 | ||
99 | void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { | 119 | static 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 | ||
117 | bool debounce_active(void) { return true; } | 137 | bool 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 | /* |
2 | Copyright 2017 Alex Ong<the.onga@gmail.com> | 2 | Copyright 2017 Alex Ong<the.onga@gmail.com> |
3 | Copyright 2021 Simon Arlott | ||
3 | This program is free software: you can redistribute it and/or modify | 4 | 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 | 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 | the 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 | 45 | typedef uint8_t debounce_counter_t; |
39 | 46 | ||
47 | #if DEBOUNCE > 0 | ||
40 | static debounce_counter_t *debounce_counters; | 48 | static debounce_counter_t *debounce_counters; |
49 | static fast_timer_t last_time; | ||
41 | static bool counters_need_update; | 50 | static bool counters_need_update; |
42 | static bool matrix_need_update; | 51 | static bool matrix_need_update; |
43 | 52 | ||
44 | #define DEBOUNCE_ELAPSED 251 | 53 | #define DEBOUNCE_ELAPSED 0 |
45 | #define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) | ||
46 | |||
47 | static 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 | ||
57 | void update_debounce_counters(uint8_t num_rows, uint8_t current_time); | 55 | static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); |
58 | void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); | 56 | static 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 |
61 | void debounce_init(uint8_t num_rows) { | 59 | void debounce_init(uint8_t num_rows) { |
@@ -68,27 +66,51 @@ void debounce_init(uint8_t num_rows) { | |||
68 | } | 66 | } |
69 | } | 67 | } |
70 | 68 | ||
69 | void debounce_free(void) { | ||
70 | free(debounce_counters); | ||
71 | debounce_counters = NULL; | ||
72 | } | ||
73 | |||
71 | void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 74 | void 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. |
83 | void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { | 102 | static 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; |
101 | void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { | 123 | static 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 | ||
124 | bool debounce_active(void) { return true; } | 143 | bool 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 | /* |
2 | Copyright 2019 Alex Ong<the.onga@gmail.com> | 2 | Copyright 2019 Alex Ong<the.onga@gmail.com> |
3 | Copyright 2021 Simon Arlott | ||
3 | This program is free software: you can redistribute it and/or modify | 4 | 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 | 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 | the 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 | |||
43 | typedef uint8_t debounce_counter_t; | ||
44 | |||
45 | #if DEBOUNCE > 0 | ||
37 | static bool matrix_need_update; | 46 | static bool matrix_need_update; |
38 | 47 | ||
39 | static debounce_counter_t *debounce_counters; | 48 | static debounce_counter_t *debounce_counters; |
49 | static fast_timer_t last_time; | ||
40 | static bool counters_need_update; | 50 | static bool counters_need_update; |
41 | 51 | ||
42 | #define DEBOUNCE_ELAPSED 251 | 52 | #define DEBOUNCE_ELAPSED 0 |
43 | #define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) | ||
44 | |||
45 | static 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 | ||
55 | void update_debounce_counters(uint8_t num_rows, uint8_t current_time); | 54 | static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); |
56 | void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); | 55 | static 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 |
59 | void debounce_init(uint8_t num_rows) { | 58 | void debounce_init(uint8_t num_rows) { |
@@ -63,27 +62,50 @@ void debounce_init(uint8_t num_rows) { | |||
63 | } | 62 | } |
64 | } | 63 | } |
65 | 64 | ||
65 | void debounce_free(void) { | ||
66 | free(debounce_counters); | ||
67 | debounce_counters = NULL; | ||
68 | } | ||
69 | |||
66 | void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 70 | void 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. |
79 | void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { | 98 | static 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; |
95 | void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { | 117 | static 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 | ||
116 | bool debounce_active(void) { return true; } | 135 | bool debounce_active(void) { return true; } |
136 | #else | ||
137 | # include "none.c" | ||
138 | #endif | ||
diff --git a/quantum/debounce/tests/asym_eager_defer_pk_tests.cpp b/quantum/debounce/tests/asym_eager_defer_pk_tests.cpp new file mode 100644 index 000000000..fe374c3df --- /dev/null +++ b/quantum/debounce/tests/asym_eager_defer_pk_tests.cpp | |||
@@ -0,0 +1,374 @@ | |||
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 | TEST_F(DebounceTest, OneKeyShort1) { | ||
22 | addEvents({ /* Time, Inputs, Outputs */ | ||
23 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
24 | /* Release key after 1ms delay */ | ||
25 | {1, {{0, 1, UP}}, {}}, | ||
26 | |||
27 | /* | ||
28 | * Until the eager timer on DOWN is observed to finish, the defer timer | ||
29 | * on UP can't start. There's no workaround for this because it's not | ||
30 | * possible to debounce an event that isn't being tracked. | ||
31 | * | ||
32 | * sym_defer_pk has the same problem but the test has to track that the | ||
33 | * key changed state so the DOWN timer is always allowed to finish | ||
34 | * before starting the UP timer. | ||
35 | */ | ||
36 | {5, {}, {}}, | ||
37 | |||
38 | {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ | ||
39 | /* Press key again after 1ms delay */ | ||
40 | {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
41 | }); | ||
42 | runEvents(); | ||
43 | } | ||
44 | |||
45 | TEST_F(DebounceTest, OneKeyShort2) { | ||
46 | addEvents({ /* Time, Inputs, Outputs */ | ||
47 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
48 | /* Release key after 2ms delay */ | ||
49 | {2, {{0, 1, UP}}, {}}, | ||
50 | |||
51 | {5, {}, {}}, /* See OneKeyShort1 */ | ||
52 | |||
53 | {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ | ||
54 | /* Press key again after 1ms delay */ | ||
55 | {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
56 | }); | ||
57 | runEvents(); | ||
58 | } | ||
59 | |||
60 | TEST_F(DebounceTest, OneKeyShort3) { | ||
61 | addEvents({ /* Time, Inputs, Outputs */ | ||
62 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
63 | /* Release key after 3ms delay */ | ||
64 | {3, {{0, 1, UP}}, {}}, | ||
65 | |||
66 | {5, {}, {}}, /* See OneKeyShort1 */ | ||
67 | |||
68 | {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ | ||
69 | /* Press key again after 1ms delay */ | ||
70 | {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
71 | }); | ||
72 | runEvents(); | ||
73 | } | ||
74 | |||
75 | TEST_F(DebounceTest, OneKeyShort4) { | ||
76 | addEvents({ /* Time, Inputs, Outputs */ | ||
77 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
78 | /* Release key after 4ms delay */ | ||
79 | {4, {{0, 1, UP}}, {}}, | ||
80 | |||
81 | {5, {}, {}}, /* See OneKeyShort1 */ | ||
82 | |||
83 | {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ | ||
84 | /* Press key again after 1ms delay */ | ||
85 | {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
86 | }); | ||
87 | runEvents(); | ||
88 | } | ||
89 | |||
90 | TEST_F(DebounceTest, OneKeyShort5) { | ||
91 | addEvents({ /* Time, Inputs, Outputs */ | ||
92 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
93 | |||
94 | /* Release key after 5ms delay */ | ||
95 | {5, {{0, 1, UP}}, {}}, | ||
96 | |||
97 | {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ | ||
98 | /* Press key again after 1ms delay */ | ||
99 | {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
100 | }); | ||
101 | runEvents(); | ||
102 | } | ||
103 | |||
104 | TEST_F(DebounceTest, OneKeyShort6) { | ||
105 | addEvents({ /* Time, Inputs, Outputs */ | ||
106 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
107 | |||
108 | /* Release key after 6ms delay */ | ||
109 | {6, {{0, 1, UP}}, {}}, | ||
110 | |||
111 | {11, {}, {{0, 1, UP}}}, /* 5ms after UP at time 6 */ | ||
112 | /* Press key again after 1ms delay */ | ||
113 | {12, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
114 | }); | ||
115 | runEvents(); | ||
116 | } | ||
117 | |||
118 | TEST_F(DebounceTest, OneKeyShort7) { | ||
119 | addEvents({ /* Time, Inputs, Outputs */ | ||
120 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
121 | |||
122 | /* Release key after 7ms delay */ | ||
123 | {7, {{0, 1, UP}}, {}}, | ||
124 | |||
125 | {12, {}, {{0, 1, UP}}}, /* 5ms after UP at time 7 */ | ||
126 | /* Press key again after 1ms delay */ | ||
127 | {13, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
128 | }); | ||
129 | runEvents(); | ||
130 | } | ||
131 | |||
132 | TEST_F(DebounceTest, OneKeyShort8) { | ||
133 | addEvents({ /* Time, Inputs, Outputs */ | ||
134 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
135 | /* Release key after 1ms delay */ | ||
136 | {1, {{0, 1, UP}}, {}}, | ||
137 | |||
138 | {5, {}, {}}, /* See OneKeyShort1 */ | ||
139 | |||
140 | {10, {}, {{0, 1, UP}}}, /* 5ms after UP at time 7 */ | ||
141 | /* Press key again after 0ms delay (scan 2) */ | ||
142 | {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
143 | }); | ||
144 | runEvents(); | ||
145 | } | ||
146 | |||
147 | TEST_F(DebounceTest, OneKeyShort9) { | ||
148 | addEvents({ /* Time, Inputs, Outputs */ | ||
149 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
150 | /* Release key after 1ms delay */ | ||
151 | {1, {{0, 1, UP}}, {}}, | ||
152 | |||
153 | {5, {}, {}}, /* See OneKeyShort1 */ | ||
154 | |||
155 | /* Press key again after 0ms delay (same scan) before debounce finishes */ | ||
156 | {10, {{0, 1, DOWN}}, {}}, | ||
157 | }); | ||
158 | runEvents(); | ||
159 | } | ||
160 | |||
161 | TEST_F(DebounceTest, OneKeyBouncing1) { | ||
162 | addEvents({ /* Time, Inputs, Outputs */ | ||
163 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
164 | {1, {{0, 1, UP}}, {}}, | ||
165 | {2, {{0, 1, DOWN}}, {}}, | ||
166 | {3, {{0, 1, UP}}, {}}, | ||
167 | {4, {{0, 1, DOWN}}, {}}, | ||
168 | {5, {{0, 1, UP}}, {}}, | ||
169 | {6, {{0, 1, DOWN}}, {}}, | ||
170 | {7, {{0, 1, UP}}, {}}, | ||
171 | {8, {{0, 1, DOWN}}, {}}, | ||
172 | {9, {{0, 1, UP}}, {}}, | ||
173 | {10, {{0, 1, DOWN}}, {}}, | ||
174 | {11, {{0, 1, UP}}, {}}, | ||
175 | {12, {{0, 1, DOWN}}, {}}, | ||
176 | {13, {{0, 1, UP}}, {}}, | ||
177 | {14, {{0, 1, DOWN}}, {}}, | ||
178 | {15, {{0, 1, UP}}, {}}, | ||
179 | |||
180 | {20, {}, {{0, 1, UP}}}, | ||
181 | /* Press key again after 1ms delay */ | ||
182 | {21, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
183 | }); | ||
184 | runEvents(); | ||
185 | } | ||
186 | |||
187 | TEST_F(DebounceTest, OneKeyBouncing2) { | ||
188 | addEvents({ /* Time, Inputs, Outputs */ | ||
189 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
190 | /* Change twice in the same time period */ | ||
191 | {1, {{0, 1, UP}}, {}}, | ||
192 | {1, {{0, 1, DOWN}}, {}}, | ||
193 | /* Change three times in the same time period */ | ||
194 | {2, {{0, 1, UP}}, {}}, | ||
195 | {2, {{0, 1, DOWN}}, {}}, | ||
196 | {2, {{0, 1, UP}}, {}}, | ||
197 | /* Change twice in the same time period */ | ||
198 | {6, {{0, 1, DOWN}}, {}}, | ||
199 | {6, {{0, 1, UP}}, {}}, | ||
200 | /* Change three times in the same time period */ | ||
201 | {7, {{0, 1, DOWN}}, {}}, | ||
202 | {7, {{0, 1, UP}}, {}}, | ||
203 | {7, {{0, 1, DOWN}}, {}}, | ||
204 | /* Change twice in the same time period */ | ||
205 | {8, {{0, 1, UP}}, {}}, | ||
206 | {8, {{0, 1, DOWN}}, {}}, | ||
207 | /* Change three times in the same time period */ | ||
208 | {9, {{0, 1, UP}}, {}}, | ||
209 | {9, {{0, 1, DOWN}}, {}}, | ||
210 | {9, {{0, 1, UP}}, {}}, | ||
211 | |||
212 | {14, {}, {{0, 1, UP}}}, | ||
213 | /* Press key again after 1ms delay */ | ||
214 | {15, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
215 | }); | ||
216 | runEvents(); | ||
217 | } | ||
218 | |||
219 | TEST_F(DebounceTest, OneKeyLong) { | ||
220 | addEvents({ /* Time, Inputs, Outputs */ | ||
221 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
222 | |||
223 | {25, {{0, 1, UP}}, {}}, | ||
224 | |||
225 | {30, {}, {{0, 1, UP}}}, | ||
226 | |||
227 | {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
228 | |||
229 | {75, {{0, 1, UP}}, {}}, | ||
230 | |||
231 | {80, {}, {{0, 1, UP}}}, | ||
232 | |||
233 | {100, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
234 | }); | ||
235 | runEvents(); | ||
236 | } | ||
237 | |||
238 | TEST_F(DebounceTest, TwoKeysShort) { | ||
239 | addEvents({ /* Time, Inputs, Outputs */ | ||
240 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
241 | {1, {{0, 2, DOWN}}, {{0, 2, DOWN}}}, | ||
242 | /* Release key after 2ms delay */ | ||
243 | {2, {{0, 1, UP}}, {}}, | ||
244 | {3, {{0, 2, UP}}, {}}, | ||
245 | |||
246 | {5, {}, {}}, /* See OneKeyShort1 */ | ||
247 | {6, {}, {}}, /* See OneKeyShort1 */ | ||
248 | |||
249 | {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ | ||
250 | /* Press key again after 1ms delay */ | ||
251 | {11, {{0, 1, DOWN}}, {{0, 1, DOWN}, {0, 2, UP}}}, /* 5ms+5ms after DOWN at time 0 */ | ||
252 | {12, {{0, 2, DOWN}}, {{0, 2, DOWN}}}, /* 5ms+5ms after DOWN at time 0 */ | ||
253 | }); | ||
254 | runEvents(); | ||
255 | } | ||
256 | |||
257 | |||
258 | TEST_F(DebounceTest, OneKeyDelayedScan1) { | ||
259 | addEvents({ /* Time, Inputs, Outputs */ | ||
260 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
261 | |||
262 | /* Processing is very late, immediately release key */ | ||
263 | {300, {{0, 1, UP}}, {}}, | ||
264 | |||
265 | {305, {}, {{0, 1, UP}}}, | ||
266 | }); | ||
267 | time_jumps_ = true; | ||
268 | runEvents(); | ||
269 | } | ||
270 | |||
271 | TEST_F(DebounceTest, OneKeyDelayedScan2) { | ||
272 | addEvents({ /* Time, Inputs, Outputs */ | ||
273 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
274 | |||
275 | /* Processing is very late, immediately release key */ | ||
276 | {300, {{0, 1, UP}}, {}}, | ||
277 | |||
278 | /* Processing is very late again */ | ||
279 | {600, {}, {{0, 1, UP}}}, | ||
280 | }); | ||
281 | time_jumps_ = true; | ||
282 | runEvents(); | ||
283 | } | ||
284 | |||
285 | TEST_F(DebounceTest, OneKeyDelayedScan3) { | ||
286 | addEvents({ /* Time, Inputs, Outputs */ | ||
287 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
288 | |||
289 | /* Processing is very late */ | ||
290 | {300, {}, {}}, | ||
291 | /* Release key after 1ms */ | ||
292 | {301, {{0, 1, UP}}, {}}, | ||
293 | |||
294 | {306, {}, {{0, 1, UP}}}, | ||
295 | }); | ||
296 | time_jumps_ = true; | ||
297 | runEvents(); | ||
298 | } | ||
299 | |||
300 | TEST_F(DebounceTest, OneKeyDelayedScan4) { | ||
301 | addEvents({ /* Time, Inputs, Outputs */ | ||
302 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
303 | |||
304 | /* Processing is very late */ | ||
305 | {300, {}, {}}, | ||
306 | /* Release key after 1ms */ | ||
307 | {301, {{0, 1, UP}}, {}}, | ||
308 | |||
309 | /* Processing is very late again */ | ||
310 | {600, {}, {{0, 1, UP}}}, | ||
311 | }); | ||
312 | time_jumps_ = true; | ||
313 | runEvents(); | ||
314 | } | ||
315 | |||
316 | TEST_F(DebounceTest, OneKeyDelayedScan5) { | ||
317 | addEvents({ /* Time, Inputs, Outputs */ | ||
318 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
319 | |||
320 | {5, {{0, 1, UP}}, {}}, | ||
321 | |||
322 | /* Processing is very late */ | ||
323 | {300, {}, {{0, 1, UP}}}, | ||
324 | /* Immediately press key again */ | ||
325 | {300, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
326 | }); | ||
327 | time_jumps_ = true; | ||
328 | runEvents(); | ||
329 | } | ||
330 | |||
331 | TEST_F(DebounceTest, OneKeyDelayedScan6) { | ||
332 | addEvents({ /* Time, Inputs, Outputs */ | ||
333 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
334 | |||
335 | {5, {{0, 1, UP}}, {}}, | ||
336 | |||
337 | /* Processing is very late */ | ||
338 | {300, {}, {{0, 1, UP}}}, | ||
339 | |||
340 | /* Press key again after 1ms */ | ||
341 | {301, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
342 | }); | ||
343 | time_jumps_ = true; | ||
344 | runEvents(); | ||
345 | } | ||
346 | |||
347 | TEST_F(DebounceTest, OneKeyDelayedScan7) { | ||
348 | addEvents({ /* Time, Inputs, Outputs */ | ||
349 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
350 | |||
351 | {5, {{0, 1, UP}}, {}}, | ||
352 | |||
353 | /* Press key again before debounce expires */ | ||
354 | {300, {{0, 1, DOWN}}, {}}, | ||
355 | }); | ||
356 | time_jumps_ = true; | ||
357 | runEvents(); | ||
358 | } | ||
359 | |||
360 | TEST_F(DebounceTest, OneKeyDelayedScan8) { | ||
361 | addEvents({ /* Time, Inputs, Outputs */ | ||
362 | {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, | ||
363 | |||
364 | /* Processing is a bit late */ | ||
365 | {50, {}, {}}, | ||
366 | /* Release key after 1ms */ | ||
367 | {51, {{0, 1, UP}}, {}}, | ||
368 | |||
369 | /* Processing is a bit late again */ | ||
370 | {100, {}, {{0, 1, UP}}}, | ||
371 | }); | ||
372 | time_jumps_ = true; | ||
373 | runEvents(); | ||
374 | } | ||
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 | |||
25 | extern "C" { | ||
26 | #include "quantum.h" | ||
27 | #include "timer.h" | ||
28 | #include "debounce.h" | ||
29 | |||
30 | void set_time(uint32_t t); | ||
31 | void advance_time(uint32_t ms); | ||
32 | } | ||
33 | |||
34 | void DebounceTest::addEvents(std::initializer_list<DebounceTestEvent> events) { | ||
35 | events_.insert(events_.end(), events.begin(), events.end()); | ||
36 | } | ||
37 | |||
38 | void 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 | |||
55 | void 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 | |||
129 | void 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 | |||
142 | void 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 | |||
151 | std::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 | |||
161 | std::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 | |||
182 | bool DebounceTest::directionValue(Direction direction) { | ||
183 | switch (direction) { | ||
184 | case DOWN: | ||
185 | return true; | ||
186 | |||
187 | case UP: | ||
188 | return false; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | std::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 */ | ||
203 | void 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 | |||
221 | DebounceTestEvent::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 | |||
227 | MatrixTestEvent::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 | |||
23 | extern "C" { | ||
24 | #include "quantum.h" | ||
25 | #include "timer.h" | ||
26 | } | ||
27 | |||
28 | enum Direction { | ||
29 | DOWN, | ||
30 | UP, | ||
31 | }; | ||
32 | |||
33 | class MatrixTestEvent { | ||
34 | public: | ||
35 | MatrixTestEvent(int row, int col, Direction direction); | ||
36 | |||
37 | const int row_; | ||
38 | const int col_; | ||
39 | const Direction direction_; | ||
40 | }; | ||
41 | |||
42 | class DebounceTestEvent { | ||
43 | public: | ||
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 | |||
54 | class DebounceTest : public ::testing::Test { | ||
55 | protected: | ||
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 | |||
62 | private: | ||
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..66928d7eb --- /dev/null +++ b/quantum/debounce/tests/rules.mk | |||
@@ -0,0 +1,44 @@ | |||
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 | DEBOUNCE_COMMON_DEFS := -DMATRIX_ROWS=4 -DMATRIX_COLS=10 -DDEBOUNCE=5 | ||
17 | |||
18 | DEBOUNCE_COMMON_SRC := $(QUANTUM_PATH)/debounce/tests/debounce_test_common.cpp \ | ||
19 | $(TMK_PATH)/common/test/timer.c | ||
20 | |||
21 | debounce_sym_defer_g_DEFS := $(DEBOUNCE_COMMON_DEFS) | ||
22 | debounce_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 | |||
26 | debounce_sym_defer_pk_DEFS := $(DEBOUNCE_COMMON_DEFS) | ||
27 | debounce_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 | |||
31 | debounce_sym_eager_pk_DEFS := $(DEBOUNCE_COMMON_DEFS) | ||
32 | debounce_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 | |||
36 | debounce_sym_eager_pr_DEFS := $(DEBOUNCE_COMMON_DEFS) | ||
37 | debounce_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 | ||
40 | |||
41 | debounce_asym_eager_defer_pk_DEFS := $(DEBOUNCE_COMMON_DEFS) | ||
42 | debounce_asym_eager_defer_pk_SRC := $(DEBOUNCE_COMMON_SRC) \ | ||
43 | $(QUANTUM_PATH)/debounce/asym_eager_defer_pk.c \ | ||
44 | $(QUANTUM_PATH)/debounce/tests/asym_eager_defer_pk_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 | |||
21 | TEST_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 | |||
34 | TEST_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 | |||
47 | TEST_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 | |||
60 | TEST_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 | |||
69 | TEST_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 | |||
82 | TEST_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 | |||
96 | TEST_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 | |||
110 | TEST_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 | |||
127 | TEST_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 | |||
142 | TEST_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 | |||
154 | TEST_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 | |||
169 | TEST_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 | |||
184 | TEST_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 | |||
199 | TEST_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 | |||
210 | TEST_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 | |||
21 | TEST_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 | |||
34 | TEST_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 | |||
47 | TEST_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 | |||
60 | TEST_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 | |||
69 | TEST_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 | |||
82 | TEST_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 | |||
96 | TEST_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 | |||
110 | TEST_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 | |||
127 | TEST_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 | |||
144 | TEST_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 | |||
156 | TEST_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 | |||
171 | TEST_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 | |||
186 | TEST_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 | |||
201 | TEST_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 | |||
212 | TEST_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 | |||
21 | TEST_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 | |||
34 | TEST_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 | |||
47 | TEST_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 | |||
60 | TEST_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 | |||
73 | TEST_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 | |||
85 | TEST_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 | |||
97 | TEST_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 | |||
112 | TEST_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 | |||
137 | TEST_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 | |||
148 | TEST_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 | |||
169 | TEST_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 | |||
180 | TEST_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 | |||
192 | TEST_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 | |||
204 | TEST_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 | |||
215 | TEST_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 | |||
227 | TEST_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 | |||
21 | TEST_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 | |||
34 | TEST_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 | |||
47 | TEST_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 | |||
60 | TEST_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 | |||
73 | TEST_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 | |||
85 | TEST_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 | |||
97 | TEST_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 | |||
112 | TEST_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 | |||
137 | TEST_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 | |||
148 | TEST_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 | |||
169 | TEST_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 | |||
192 | TEST_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 | |||
204 | TEST_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 | |||
212 | TEST_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 | |||
223 | TEST_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 | |||
235 | TEST_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 | |||
247 | TEST_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 | |||
258 | TEST_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 | |||
270 | TEST_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..c54c45aa6 --- /dev/null +++ b/quantum/debounce/tests/testlist.mk | |||
@@ -0,0 +1,6 @@ | |||
1 | TEST_LIST += \ | ||
2 | debounce_sym_defer_g \ | ||
3 | debounce_sym_defer_pk \ | ||
4 | debounce_sym_eager_pk \ | ||
5 | debounce_sym_eager_pr \ | ||
6 | debounce_asym_eager_defer_pk | ||
diff --git a/quantum/led_matrix.c b/quantum/led_matrix.c index 7e0fdf896..e7e933e1d 100644 --- a/quantum/led_matrix.c +++ b/quantum/led_matrix.c | |||
@@ -33,6 +33,14 @@ const led_point_t k_led_matrix_center = {112, 32}; | |||
33 | const led_point_t k_led_matrix_center = LED_MATRIX_CENTER; | 33 | const led_point_t k_led_matrix_center = LED_MATRIX_CENTER; |
34 | #endif | 34 | #endif |
35 | 35 | ||
36 | // clang-format off | ||
37 | #ifndef LED_MATRIX_IMMEDIATE_EEPROM | ||
38 | # define led_eeconfig_update(v) led_update_eeprom |= v | ||
39 | #else | ||
40 | # define led_eeconfig_update(v) if (v) eeconfig_update_led_matrix() | ||
41 | #endif | ||
42 | // clang-format on | ||
43 | |||
36 | // Generic effect runners | 44 | // Generic effect runners |
37 | #include "led_matrix_runners/effect_runner_dx_dy_dist.h" | 45 | #include "led_matrix_runners/effect_runner_dx_dy_dist.h" |
38 | #include "led_matrix_runners/effect_runner_dx_dy.h" | 46 | #include "led_matrix_runners/effect_runner_dx_dy.h" |
@@ -67,10 +75,6 @@ const led_point_t k_led_matrix_center = LED_MATRIX_CENTER; | |||
67 | # define LED_DISABLE_TIMEOUT 0 | 75 | # define LED_DISABLE_TIMEOUT 0 |
68 | #endif | 76 | #endif |
69 | 77 | ||
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 | 78 | #if !defined(LED_MATRIX_MAXIMUM_BRIGHTNESS) || LED_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX |
75 | # undef LED_MATRIX_MAXIMUM_BRIGHTNESS | 79 | # undef LED_MATRIX_MAXIMUM_BRIGHTNESS |
76 | # define LED_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX | 80 | # define LED_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX |
@@ -108,6 +112,7 @@ last_hit_t g_last_hit_tracker; | |||
108 | 112 | ||
109 | // internals | 113 | // internals |
110 | static bool suspend_state = false; | 114 | static bool suspend_state = false; |
115 | static bool led_update_eeprom = false; | ||
111 | static uint8_t led_last_enable = UINT8_MAX; | 116 | static uint8_t led_last_enable = UINT8_MAX; |
112 | static uint8_t led_last_effect = UINT8_MAX; | 117 | static uint8_t led_last_effect = UINT8_MAX; |
113 | static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false}; | 118 | static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false}; |
@@ -280,6 +285,7 @@ static void led_task_timers(void) { | |||
280 | 285 | ||
281 | static void led_task_sync(void) { | 286 | static void led_task_sync(void) { |
282 | // next task | 287 | // next task |
288 | if (led_update_eeprom) eeconfig_update_led_matrix(); | ||
283 | if (sync_timer_elapsed32(g_led_timer) >= LED_MATRIX_LED_FLUSH_LIMIT) led_task_state = STARTING; | 289 | if (sync_timer_elapsed32(g_led_timer) >= LED_MATRIX_LED_FLUSH_LIMIT) led_task_state = STARTING; |
284 | } | 290 | } |
285 | 291 | ||
@@ -469,9 +475,7 @@ bool led_matrix_get_suspend_state(void) { return suspend_state; } | |||
469 | void led_matrix_toggle_eeprom_helper(bool write_to_eeprom) { | 475 | void led_matrix_toggle_eeprom_helper(bool write_to_eeprom) { |
470 | led_matrix_eeconfig.enable ^= 1; | 476 | led_matrix_eeconfig.enable ^= 1; |
471 | led_task_state = STARTING; | 477 | led_task_state = STARTING; |
472 | if (write_to_eeprom) { | 478 | led_eeconfig_update(write_to_eeprom); |
473 | eeconfig_update_led_matrix(); | ||
474 | } | ||
475 | dprintf("led matrix toggle [%s]: led_matrix_eeconfig.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.enable); | 479 | dprintf("led matrix toggle [%s]: led_matrix_eeconfig.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.enable); |
476 | } | 480 | } |
477 | void led_matrix_toggle_noeeprom(void) { led_matrix_toggle_eeprom_helper(false); } | 481 | void led_matrix_toggle_noeeprom(void) { led_matrix_toggle_eeprom_helper(false); } |
@@ -479,7 +483,7 @@ void led_matrix_toggle(void) { led_matrix_toggle_eeprom_helper(true); } | |||
479 | 483 | ||
480 | void led_matrix_enable(void) { | 484 | void led_matrix_enable(void) { |
481 | led_matrix_enable_noeeprom(); | 485 | led_matrix_enable_noeeprom(); |
482 | eeconfig_update_led_matrix(); | 486 | led_eeconfig_update(true); |
483 | } | 487 | } |
484 | 488 | ||
485 | void led_matrix_enable_noeeprom(void) { | 489 | void led_matrix_enable_noeeprom(void) { |
@@ -489,7 +493,7 @@ void led_matrix_enable_noeeprom(void) { | |||
489 | 493 | ||
490 | void led_matrix_disable(void) { | 494 | void led_matrix_disable(void) { |
491 | led_matrix_disable_noeeprom(); | 495 | led_matrix_disable_noeeprom(); |
492 | eeconfig_update_led_matrix(); | 496 | led_eeconfig_update(true); |
493 | } | 497 | } |
494 | 498 | ||
495 | void led_matrix_disable_noeeprom(void) { | 499 | void led_matrix_disable_noeeprom(void) { |
@@ -511,9 +515,7 @@ void led_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) { | |||
511 | led_matrix_eeconfig.mode = mode; | 515 | led_matrix_eeconfig.mode = mode; |
512 | } | 516 | } |
513 | led_task_state = STARTING; | 517 | led_task_state = STARTING; |
514 | if (write_to_eeprom) { | 518 | led_eeconfig_update(write_to_eeprom); |
515 | eeconfig_update_led_matrix(); | ||
516 | } | ||
517 | dprintf("led matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.mode); | 519 | dprintf("led matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.mode); |
518 | } | 520 | } |
519 | void led_matrix_mode_noeeprom(uint8_t mode) { led_matrix_mode_eeprom_helper(mode, false); } | 521 | void led_matrix_mode_noeeprom(uint8_t mode) { led_matrix_mode_eeprom_helper(mode, false); } |
@@ -540,9 +542,7 @@ void led_matrix_set_val_eeprom_helper(uint8_t val, bool write_to_eeprom) { | |||
540 | return; | 542 | return; |
541 | } | 543 | } |
542 | led_matrix_eeconfig.val = (val > LED_MATRIX_MAXIMUM_BRIGHTNESS) ? LED_MATRIX_MAXIMUM_BRIGHTNESS : val; | 544 | led_matrix_eeconfig.val = (val > LED_MATRIX_MAXIMUM_BRIGHTNESS) ? LED_MATRIX_MAXIMUM_BRIGHTNESS : val; |
543 | if (write_to_eeprom) { | 545 | led_eeconfig_update(write_to_eeprom); |
544 | eeconfig_update_led_matrix(); | ||
545 | } | ||
546 | dprintf("led matrix set val [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.val); | 546 | dprintf("led matrix set val [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.val); |
547 | } | 547 | } |
548 | void led_matrix_set_val_noeeprom(uint8_t val) { led_matrix_set_val_eeprom_helper(val, false); } | 548 | void led_matrix_set_val_noeeprom(uint8_t val) { led_matrix_set_val_eeprom_helper(val, false); } |
@@ -560,9 +560,7 @@ void led_matrix_decrease_val(void) { led_matrix_decrease_val_helper(true); } | |||
560 | 560 | ||
561 | void led_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) { | 561 | void led_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) { |
562 | led_matrix_eeconfig.speed = speed; | 562 | led_matrix_eeconfig.speed = speed; |
563 | if (write_to_eeprom) { | 563 | led_eeconfig_update(write_to_eeprom); |
564 | eeconfig_update_led_matrix(); | ||
565 | } | ||
566 | dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.speed); | 564 | dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.speed); |
567 | } | 565 | } |
568 | void led_matrix_set_speed_noeeprom(uint8_t speed) { led_matrix_set_speed_eeprom_helper(speed, false); } | 566 | void led_matrix_set_speed_noeeprom(uint8_t speed) { led_matrix_set_speed_eeprom_helper(speed, false); } |
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 |
25 | static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS; | 26 | static 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 | ||
27 | static const pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; | 29 | static const pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; |
30 | # endif // MATRIX_ROW_PINS | ||
31 | # ifdef MATRIX_COL_PINS | ||
28 | static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; | 32 | static 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) */ |
32 | extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values | 37 | extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values |
33 | extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values | 38 | extern 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 | |||
35 | static inline void setPinOutput_writeLow(pin_t pin) { | 45 | static 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 | ||
50 | static 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 | ||
61 | static 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 | ||
83 | static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); } | 90 | static 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 | ||
93 | static 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 | ||
100 | static 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 | ||
131 | static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); } | 134 | static 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 | ||
141 | static 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 | ||
148 | static 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 | ||
191 | void matrix_init(void) { | 181 | void 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 | ||
206 | uint8_t matrix_scan(void) { | 196 | uint8_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/quantum.h b/quantum/quantum.h index e4a7c5723..66ba96fde 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h | |||
@@ -176,6 +176,10 @@ extern layer_state_t layer_state; | |||
176 | # include "oled_driver.h" | 176 | # include "oled_driver.h" |
177 | #endif | 177 | #endif |
178 | 178 | ||
179 | #ifdef ST7565_ENABLE | ||
180 | # include "st7565.h" | ||
181 | #endif | ||
182 | |||
179 | #ifdef DIP_SWITCH_ENABLE | 183 | #ifdef DIP_SWITCH_ENABLE |
180 | # include "dip_switch.h" | 184 | # include "dip_switch.h" |
181 | #endif | 185 | #endif |
diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c index ab8dbd849..3c5ddba93 100644 --- a/quantum/rgb_matrix.c +++ b/quantum/rgb_matrix.c | |||
@@ -31,6 +31,14 @@ const led_point_t k_rgb_matrix_center = {112, 32}; | |||
31 | const led_point_t k_rgb_matrix_center = RGB_MATRIX_CENTER; | 31 | const led_point_t k_rgb_matrix_center = RGB_MATRIX_CENTER; |
32 | #endif | 32 | #endif |
33 | 33 | ||
34 | // clang-format off | ||
35 | #ifndef RGB_MATRIX_IMMEDIATE_EEPROM | ||
36 | # define rgb_eeconfig_update(v) rgb_update_eeprom |= v | ||
37 | #else | ||
38 | # define rgb_eeconfig_update(v) if (v) eeconfig_update_rgb_matrix() | ||
39 | #endif | ||
40 | // clang-format on | ||
41 | |||
34 | __attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv); } | 42 | __attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv); } |
35 | 43 | ||
36 | // Generic effect runners | 44 | // Generic effect runners |
@@ -67,10 +75,6 @@ __attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv | |||
67 | # define RGB_DISABLE_TIMEOUT 0 | 75 | # define RGB_DISABLE_TIMEOUT 0 |
68 | #endif | 76 | #endif |
69 | 77 | ||
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 | 78 | #if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX |
75 | # undef RGB_MATRIX_MAXIMUM_BRIGHTNESS | 79 | # undef RGB_MATRIX_MAXIMUM_BRIGHTNESS |
76 | # define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX | 80 | # define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX |
@@ -129,6 +133,7 @@ last_hit_t g_last_hit_tracker; | |||
129 | 133 | ||
130 | // internals | 134 | // internals |
131 | static bool suspend_state = false; | 135 | static bool suspend_state = false; |
136 | static bool rgb_update_eeprom = false; | ||
132 | static uint8_t rgb_last_enable = UINT8_MAX; | 137 | static uint8_t rgb_last_enable = UINT8_MAX; |
133 | static uint8_t rgb_last_effect = UINT8_MAX; | 138 | static uint8_t rgb_last_effect = UINT8_MAX; |
134 | static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false}; | 139 | static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false}; |
@@ -315,6 +320,7 @@ static void rgb_task_timers(void) { | |||
315 | 320 | ||
316 | static void rgb_task_sync(void) { | 321 | static void rgb_task_sync(void) { |
317 | // next task | 322 | // next task |
323 | if (rgb_update_eeprom) eeconfig_update_rgb_matrix(); | ||
318 | if (sync_timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING; | 324 | if (sync_timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING; |
319 | } | 325 | } |
320 | 326 | ||
@@ -511,9 +517,7 @@ bool rgb_matrix_get_suspend_state(void) { return suspend_state; } | |||
511 | void rgb_matrix_toggle_eeprom_helper(bool write_to_eeprom) { | 517 | void rgb_matrix_toggle_eeprom_helper(bool write_to_eeprom) { |
512 | rgb_matrix_config.enable ^= 1; | 518 | rgb_matrix_config.enable ^= 1; |
513 | rgb_task_state = STARTING; | 519 | rgb_task_state = STARTING; |
514 | if (write_to_eeprom) { | 520 | rgb_eeconfig_update(write_to_eeprom); |
515 | eeconfig_update_rgb_matrix(); | ||
516 | } | ||
517 | dprintf("rgb matrix toggle [%s]: rgb_matrix_config.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.enable); | 521 | dprintf("rgb matrix toggle [%s]: rgb_matrix_config.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.enable); |
518 | } | 522 | } |
519 | void rgb_matrix_toggle_noeeprom(void) { rgb_matrix_toggle_eeprom_helper(false); } | 523 | void rgb_matrix_toggle_noeeprom(void) { rgb_matrix_toggle_eeprom_helper(false); } |
@@ -521,7 +525,7 @@ void rgb_matrix_toggle(void) { rgb_matrix_toggle_eeprom_helper(true); } | |||
521 | 525 | ||
522 | void rgb_matrix_enable(void) { | 526 | void rgb_matrix_enable(void) { |
523 | rgb_matrix_enable_noeeprom(); | 527 | rgb_matrix_enable_noeeprom(); |
524 | eeconfig_update_rgb_matrix(); | 528 | rgb_eeconfig_update(true); |
525 | } | 529 | } |
526 | 530 | ||
527 | void rgb_matrix_enable_noeeprom(void) { | 531 | void rgb_matrix_enable_noeeprom(void) { |
@@ -531,7 +535,7 @@ void rgb_matrix_enable_noeeprom(void) { | |||
531 | 535 | ||
532 | void rgb_matrix_disable(void) { | 536 | void rgb_matrix_disable(void) { |
533 | rgb_matrix_disable_noeeprom(); | 537 | rgb_matrix_disable_noeeprom(); |
534 | eeconfig_update_rgb_matrix(); | 538 | rgb_eeconfig_update(true); |
535 | } | 539 | } |
536 | 540 | ||
537 | void rgb_matrix_disable_noeeprom(void) { | 541 | void rgb_matrix_disable_noeeprom(void) { |
@@ -553,9 +557,7 @@ void rgb_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) { | |||
553 | rgb_matrix_config.mode = mode; | 557 | rgb_matrix_config.mode = mode; |
554 | } | 558 | } |
555 | rgb_task_state = STARTING; | 559 | rgb_task_state = STARTING; |
556 | if (write_to_eeprom) { | 560 | rgb_eeconfig_update(write_to_eeprom); |
557 | eeconfig_update_rgb_matrix(); | ||
558 | } | ||
559 | dprintf("rgb matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.mode); | 561 | dprintf("rgb matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.mode); |
560 | } | 562 | } |
561 | void rgb_matrix_mode_noeeprom(uint8_t mode) { rgb_matrix_mode_eeprom_helper(mode, false); } | 563 | void rgb_matrix_mode_noeeprom(uint8_t mode) { rgb_matrix_mode_eeprom_helper(mode, false); } |
@@ -584,9 +586,7 @@ void rgb_matrix_sethsv_eeprom_helper(uint16_t hue, uint8_t sat, uint8_t val, boo | |||
584 | rgb_matrix_config.hsv.h = hue; | 586 | rgb_matrix_config.hsv.h = hue; |
585 | rgb_matrix_config.hsv.s = sat; | 587 | rgb_matrix_config.hsv.s = sat; |
586 | rgb_matrix_config.hsv.v = (val > RGB_MATRIX_MAXIMUM_BRIGHTNESS) ? RGB_MATRIX_MAXIMUM_BRIGHTNESS : val; | 588 | rgb_matrix_config.hsv.v = (val > RGB_MATRIX_MAXIMUM_BRIGHTNESS) ? RGB_MATRIX_MAXIMUM_BRIGHTNESS : val; |
587 | if (write_to_eeprom) { | 589 | rgb_eeconfig_update(write_to_eeprom); |
588 | eeconfig_update_rgb_matrix(); | ||
589 | } | ||
590 | dprintf("rgb matrix set hsv [%s]: %u,%u,%u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.hsv.h, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v); | 590 | dprintf("rgb matrix set hsv [%s]: %u,%u,%u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.hsv.h, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v); |
591 | } | 591 | } |
592 | void rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) { rgb_matrix_sethsv_eeprom_helper(hue, sat, val, false); } | 592 | void rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) { rgb_matrix_sethsv_eeprom_helper(hue, sat, val, false); } |
@@ -623,9 +623,7 @@ void rgb_matrix_decrease_val(void) { rgb_matrix_decrease_val_helper(true); } | |||
623 | 623 | ||
624 | void rgb_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) { | 624 | void rgb_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) { |
625 | rgb_matrix_config.speed = speed; | 625 | rgb_matrix_config.speed = speed; |
626 | if (write_to_eeprom) { | 626 | rgb_eeconfig_update(write_to_eeprom); |
627 | eeconfig_update_rgb_matrix(); | ||
628 | } | ||
629 | dprintf("rgb matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.speed); | 627 | dprintf("rgb matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.speed); |
630 | } | 628 | } |
631 | void rgb_matrix_set_speed_noeeprom(uint8_t speed) { rgb_matrix_set_speed_eeprom_helper(speed, false); } | 629 | void rgb_matrix_set_speed_noeeprom(uint8_t speed) { rgb_matrix_set_speed_eeprom_helper(speed, false); } |
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_animations/jellybean_raindrops_anim.h b/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h index 9493b3850..a17e954b1 100644 --- a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h +++ b/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h | |||
@@ -4,7 +4,7 @@ RGB_MATRIX_EFFECT(JELLYBEAN_RAINDROPS) | |||
4 | 4 | ||
5 | static void jellybean_raindrops_set_color(int i, effect_params_t* params) { | 5 | static void jellybean_raindrops_set_color(int i, effect_params_t* params) { |
6 | if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return; | 6 | if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return; |
7 | HSV hsv = {rand() & 0xFF, rand() & 0xFF, rgb_matrix_config.hsv.v}; | 7 | HSV hsv = {rand() & 0xFF, qadd8(rand() & 0x7F, 0x80), rgb_matrix_config.hsv.v}; |
8 | RGB rgb = rgb_matrix_hsv_to_rgb(hsv); | 8 | RGB rgb = rgb_matrix_hsv_to_rgb(hsv); |
9 | rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); | 9 | rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); |
10 | } | 10 | } |
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" | ||
176 | static void init(void) { | ||
177 | spi_init(); | ||
178 | AW20216_init(); | ||
179 | } | ||
180 | |||
181 | static void flush(void) { AW20216_update_pwm_buffers(); } | ||
182 | |||
183 | const 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..56d91b07f 100644 --- a/quantum/split_common/matrix.c +++ b/quantum/split_common/matrix.c | |||
@@ -16,23 +16,30 @@ 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" |
22 | #include "quantum.h" | 23 | #include "quantum.h" |
23 | #include "split_util.h" | 24 | #include "split_util.h" |
24 | #include "config.h" | 25 | #include "config.h" |
25 | #include "transport.h" | 26 | #include "transactions.h" |
26 | 27 | ||
27 | #define ERROR_DISCONNECT_COUNT 5 | 28 | #ifndef ERROR_DISCONNECT_COUNT |
29 | # define ERROR_DISCONNECT_COUNT 5 | ||
30 | #endif // ERROR_DISCONNECT_COUNT | ||
28 | 31 | ||
29 | #define ROWS_PER_HAND (MATRIX_ROWS / 2) | 32 | #define ROWS_PER_HAND (MATRIX_ROWS / 2) |
30 | 33 | ||
31 | #ifdef DIRECT_PINS | 34 | #ifdef DIRECT_PINS |
32 | static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS; | 35 | static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS; |
33 | #elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW) | 36 | #elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW) |
37 | # ifdef MATRIX_ROW_PINS | ||
34 | static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; | 38 | static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; |
39 | # endif // MATRIX_ROW_PINS | ||
40 | # ifdef MATRIX_COL_PINS | ||
35 | static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; | 41 | static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; |
42 | # endif // MATRIX_COL_PINS | ||
36 | #endif | 43 | #endif |
37 | 44 | ||
38 | /* matrix state(1:on, 0:off) */ | 45 | /* matrix state(1:on, 0:off) */ |
@@ -45,6 +52,9 @@ uint8_t thisHand, thatHand; | |||
45 | // user-defined overridable functions | 52 | // user-defined overridable functions |
46 | __attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); } | 53 | __attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); } |
47 | __attribute__((weak)) void matrix_slave_scan_user(void) {} | 54 | __attribute__((weak)) void matrix_slave_scan_user(void) {} |
55 | __attribute__((weak)) void matrix_init_pins(void); | ||
56 | __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row); | ||
57 | __attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col); | ||
48 | 58 | ||
49 | static inline void setPinOutput_writeLow(pin_t pin) { | 59 | static inline void setPinOutput_writeLow(pin_t pin) { |
50 | ATOMIC_BLOCK_FORCEON { | 60 | ATOMIC_BLOCK_FORCEON { |
@@ -61,7 +71,7 @@ static inline void setPinInputHigh_atomic(pin_t pin) { | |||
61 | 71 | ||
62 | #ifdef DIRECT_PINS | 72 | #ifdef DIRECT_PINS |
63 | 73 | ||
64 | static void init_pins(void) { | 74 | __attribute__((weak)) void matrix_init_pins(void) { |
65 | for (int row = 0; row < MATRIX_ROWS; row++) { | 75 | for (int row = 0; row < MATRIX_ROWS; row++) { |
66 | for (int col = 0; col < MATRIX_COLS; col++) { | 76 | for (int col = 0; col < MATRIX_COLS; col++) { |
67 | pin_t pin = direct_pins[row][col]; | 77 | pin_t pin = direct_pins[row][col]; |
@@ -72,7 +82,7 @@ static void init_pins(void) { | |||
72 | } | 82 | } |
73 | } | 83 | } |
74 | 84 | ||
75 | static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { | 85 | __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { |
76 | // Start with a clear matrix row | 86 | // Start with a clear matrix row |
77 | matrix_row_t current_row_value = 0; | 87 | matrix_row_t current_row_value = 0; |
78 | 88 | ||
@@ -83,16 +93,13 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) | |||
83 | } | 93 | } |
84 | } | 94 | } |
85 | 95 | ||
86 | // If the row has changed, store the row and return the changed flag. | 96 | // Update the matrix |
87 | if (current_matrix[current_row] != current_row_value) { | 97 | current_matrix[current_row] = current_row_value; |
88 | current_matrix[current_row] = current_row_value; | ||
89 | return true; | ||
90 | } | ||
91 | return false; | ||
92 | } | 98 | } |
93 | 99 | ||
94 | #elif defined(DIODE_DIRECTION) | 100 | #elif defined(DIODE_DIRECTION) |
95 | # if (DIODE_DIRECTION == COL2ROW) | 101 | # if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS) |
102 | # if (DIODE_DIRECTION == COL2ROW) | ||
96 | 103 | ||
97 | static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); } | 104 | static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); } |
98 | 105 | ||
@@ -104,14 +111,14 @@ static void unselect_rows(void) { | |||
104 | } | 111 | } |
105 | } | 112 | } |
106 | 113 | ||
107 | static void init_pins(void) { | 114 | __attribute__((weak)) void matrix_init_pins(void) { |
108 | unselect_rows(); | 115 | unselect_rows(); |
109 | for (uint8_t x = 0; x < MATRIX_COLS; x++) { | 116 | for (uint8_t x = 0; x < MATRIX_COLS; x++) { |
110 | setPinInputHigh_atomic(col_pins[x]); | 117 | setPinInputHigh_atomic(col_pins[x]); |
111 | } | 118 | } |
112 | } | 119 | } |
113 | 120 | ||
114 | static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { | 121 | __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { |
115 | // Start with a clear matrix row | 122 | // Start with a clear matrix row |
116 | matrix_row_t current_row_value = 0; | 123 | matrix_row_t current_row_value = 0; |
117 | 124 | ||
@@ -132,15 +139,11 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) | |||
132 | unselect_row(current_row); | 139 | unselect_row(current_row); |
133 | matrix_output_unselect_delay(); // wait for all Col signals to go HIGH | 140 | matrix_output_unselect_delay(); // wait for all Col signals to go HIGH |
134 | 141 | ||
135 | // If the row has changed, store the row and return the changed flag. | 142 | // Update the matrix |
136 | if (current_matrix[current_row] != current_row_value) { | 143 | current_matrix[current_row] = current_row_value; |
137 | current_matrix[current_row] = current_row_value; | ||
138 | return true; | ||
139 | } | ||
140 | return false; | ||
141 | } | 144 | } |
142 | 145 | ||
143 | # elif (DIODE_DIRECTION == ROW2COL) | 146 | # elif (DIODE_DIRECTION == ROW2COL) |
144 | 147 | ||
145 | static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); } | 148 | static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); } |
146 | 149 | ||
@@ -152,52 +155,39 @@ static void unselect_cols(void) { | |||
152 | } | 155 | } |
153 | } | 156 | } |
154 | 157 | ||
155 | static void init_pins(void) { | 158 | __attribute__((weak)) void matrix_init_pins(void) { |
156 | unselect_cols(); | 159 | unselect_cols(); |
157 | for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { | 160 | for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { |
158 | setPinInputHigh_atomic(row_pins[x]); | 161 | setPinInputHigh_atomic(row_pins[x]); |
159 | } | 162 | } |
160 | } | 163 | } |
161 | 164 | ||
162 | static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { | 165 | __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 | 166 | // Select col |
166 | select_col(current_col); | 167 | select_col(current_col); |
167 | matrix_output_select_delay(); | 168 | matrix_output_select_delay(); |
168 | 169 | ||
169 | // For each row... | 170 | // For each row... |
170 | for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) { | 171 | 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 | 172 | // Check row pin state |
176 | if (readPin(row_pins[row_index]) == 0) { | 173 | if (readPin(row_pins[row_index]) == 0) { |
177 | // Pin LO, set col bit | 174 | // Pin LO, set col bit |
178 | current_row_value |= (MATRIX_ROW_SHIFTER << current_col); | 175 | current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col); |
179 | } else { | 176 | } else { |
180 | // Pin HI, clear col bit | 177 | // Pin HI, clear col bit |
181 | current_row_value &= ~(MATRIX_ROW_SHIFTER << current_col); | 178 | 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 | } | 179 | } |
189 | } | 180 | } |
190 | 181 | ||
191 | // Unselect col | 182 | // Unselect col |
192 | unselect_col(current_col); | 183 | unselect_col(current_col); |
193 | matrix_output_unselect_delay(); // wait for all Row signals to go HIGH | 184 | matrix_output_unselect_delay(); // wait for all Row signals to go HIGH |
194 | |||
195 | return matrix_changed; | ||
196 | } | 185 | } |
197 | 186 | ||
198 | # else | 187 | # else |
199 | # error DIODE_DIRECTION must be one of COL2ROW or ROW2COL! | 188 | # error DIODE_DIRECTION must be one of COL2ROW or ROW2COL! |
200 | # endif | 189 | # endif |
190 | # endif // defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS) | ||
201 | #else | 191 | #else |
202 | # error DIODE_DIRECTION is not defined! | 192 | # error DIODE_DIRECTION is not defined! |
203 | #endif | 193 | #endif |
@@ -233,7 +223,7 @@ void matrix_init(void) { | |||
233 | thatHand = ROWS_PER_HAND - thisHand; | 223 | thatHand = ROWS_PER_HAND - thisHand; |
234 | 224 | ||
235 | // initialize key pins | 225 | // initialize key pins |
236 | init_pins(); | 226 | matrix_init_pins(); |
237 | 227 | ||
238 | // initialize matrix state: all keys off | 228 | // initialize matrix state: all keys off |
239 | for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | 229 | for (uint8_t i = 0; i < MATRIX_ROWS; i++) { |
@@ -288,20 +278,23 @@ bool matrix_post_scan(void) { | |||
288 | } | 278 | } |
289 | 279 | ||
290 | uint8_t matrix_scan(void) { | 280 | uint8_t matrix_scan(void) { |
291 | bool local_changed = false; | 281 | matrix_row_t curr_matrix[MATRIX_ROWS] = {0}; |
292 | 282 | ||
293 | #if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) | 283 | #if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) |
294 | // Set row, read cols | 284 | // Set row, read cols |
295 | for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) { | 285 | for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) { |
296 | local_changed |= read_cols_on_row(raw_matrix, current_row); | 286 | matrix_read_cols_on_row(curr_matrix, current_row); |
297 | } | 287 | } |
298 | #elif (DIODE_DIRECTION == ROW2COL) | 288 | #elif (DIODE_DIRECTION == ROW2COL) |
299 | // Set col, read rows | 289 | // Set col, read rows |
300 | for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { | 290 | for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { |
301 | local_changed |= read_rows_on_col(raw_matrix, current_col); | 291 | matrix_read_rows_on_col(curr_matrix, current_col); |
302 | } | 292 | } |
303 | #endif | 293 | #endif |
304 | 294 | ||
295 | bool local_changed = memcmp(raw_matrix, curr_matrix, sizeof(curr_matrix)) != 0; | ||
296 | if (local_changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix)); | ||
297 | |||
305 | debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed); | 298 | debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed); |
306 | 299 | ||
307 | bool remote_changed = matrix_post_scan(); | 300 | bool remote_changed = matrix_post_scan(); |
diff --git a/quantum/split_common/post_config.h b/quantum/split_common/post_config.h index 4ae1d5273..a4c0a1956 100644 --- a/quantum/split_common/post_config.h +++ b/quantum/split_common/post_config.h | |||
@@ -7,13 +7,4 @@ | |||
7 | # ifndef F_SCL | 7 | # ifndef F_SCL |
8 | # define F_SCL 100000UL // SCL frequency | 8 | # define F_SCL 100000UL // SCL frequency |
9 | # endif | 9 | # endif |
10 | |||
11 | #else // use serial | ||
12 | // When using serial, the user must define RGBLIGHT_SPLIT explicitly | ||
13 | // in config.h as needed. | ||
14 | // see quantum/rgblight_post_config.h | ||
15 | # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
16 | // When using serial and RGBLIGHT_SPLIT need separate transaction | ||
17 | # define SERIAL_USE_MULTI_TRANSACTION | ||
18 | # endif | ||
19 | #endif | 10 | #endif |
diff --git a/quantum/split_common/transaction_id_define.h b/quantum/split_common/transaction_id_define.h new file mode 100644 index 000000000..464c73478 --- /dev/null +++ b/quantum/split_common/transaction_id_define.h | |||
@@ -0,0 +1,94 @@ | |||
1 | /* Copyright 2021 QMK | ||
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 | #pragma once | ||
18 | |||
19 | enum serial_transaction_id { | ||
20 | #ifdef USE_I2C | ||
21 | I2C_EXECUTE_CALLBACK, | ||
22 | #endif // USE_I2C | ||
23 | |||
24 | GET_SLAVE_MATRIX_CHECKSUM, | ||
25 | GET_SLAVE_MATRIX_DATA, | ||
26 | |||
27 | #ifdef SPLIT_TRANSPORT_MIRROR | ||
28 | PUT_MASTER_MATRIX, | ||
29 | #endif // SPLIT_TRANSPORT_MIRROR | ||
30 | |||
31 | #ifdef ENCODER_ENABLE | ||
32 | GET_ENCODERS_CHECKSUM, | ||
33 | GET_ENCODERS_DATA, | ||
34 | #endif // ENCODER_ENABLE | ||
35 | |||
36 | #ifndef DISABLE_SYNC_TIMER | ||
37 | PUT_SYNC_TIMER, | ||
38 | #endif // DISABLE_SYNC_TIMER | ||
39 | |||
40 | #if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) | ||
41 | PUT_LAYER_STATE, | ||
42 | PUT_DEFAULT_LAYER_STATE, | ||
43 | #endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) | ||
44 | |||
45 | #ifdef SPLIT_LED_STATE_ENABLE | ||
46 | PUT_LED_STATE, | ||
47 | #endif // SPLIT_LED_STATE_ENABLE | ||
48 | |||
49 | #ifdef SPLIT_MODS_ENABLE | ||
50 | PUT_MODS, | ||
51 | #endif // SPLIT_MODS_ENABLE | ||
52 | |||
53 | #ifdef BACKLIGHT_ENABLE | ||
54 | PUT_BACKLIGHT, | ||
55 | #endif // BACKLIGHT_ENABLE | ||
56 | |||
57 | #if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
58 | PUT_RGBLIGHT, | ||
59 | #endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
60 | |||
61 | #if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
62 | PUT_LED_MATRIX, | ||
63 | #endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
64 | |||
65 | #if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
66 | PUT_RGB_MATRIX, | ||
67 | #endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
68 | |||
69 | #if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) | ||
70 | PUT_WPM, | ||
71 | #endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) | ||
72 | |||
73 | #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
74 | PUT_RPC_INFO, | ||
75 | PUT_RPC_REQ_DATA, | ||
76 | EXECUTE_RPC, | ||
77 | GET_RPC_RESP_DATA, | ||
78 | #endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
79 | |||
80 | // keyboard-specific | ||
81 | #ifdef SPLIT_TRANSACTION_IDS_KB | ||
82 | SPLIT_TRANSACTION_IDS_KB, | ||
83 | #endif // SPLIT_TRANSACTION_IDS_KB | ||
84 | |||
85 | // user/keymap-specific | ||
86 | #ifdef SPLIT_TRANSACTION_IDS_USER | ||
87 | SPLIT_TRANSACTION_IDS_USER, | ||
88 | #endif // SPLIT_TRANSACTION_IDS_USER | ||
89 | |||
90 | NUM_TOTAL_TRANSACTIONS | ||
91 | }; | ||
92 | |||
93 | // Ensure we only use 5 bits for transaction | ||
94 | _Static_assert(NUM_TOTAL_TRANSACTIONS <= (1 << 5), "Max number of usable transactions exceeded"); | ||
diff --git a/quantum/split_common/transactions.c b/quantum/split_common/transactions.c new file mode 100644 index 000000000..99a8623b3 --- /dev/null +++ b/quantum/split_common/transactions.c | |||
@@ -0,0 +1,670 @@ | |||
1 | /* Copyright 2021 QMK | ||
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 <string.h> | ||
18 | #include <stddef.h> | ||
19 | |||
20 | #include "debug.h" | ||
21 | #include "matrix.h" | ||
22 | #include "quantum.h" | ||
23 | #include "transactions.h" | ||
24 | #include "transport.h" | ||
25 | #include "transaction_id_define.h" | ||
26 | |||
27 | #define SYNC_TIMER_OFFSET 2 | ||
28 | |||
29 | #ifndef FORCED_SYNC_THROTTLE_MS | ||
30 | # define FORCED_SYNC_THROTTLE_MS 100 | ||
31 | #endif // FORCED_SYNC_THROTTLE_MS | ||
32 | |||
33 | #define sizeof_member(type, member) sizeof(((type *)NULL)->member) | ||
34 | |||
35 | #define trans_initiator2target_initializer_cb(member, cb) \ | ||
36 | { &dummy, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb } | ||
37 | #define trans_initiator2target_initializer(member) trans_initiator2target_initializer_cb(member, NULL) | ||
38 | |||
39 | #define trans_target2initiator_initializer_cb(member, cb) \ | ||
40 | { &dummy, 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb } | ||
41 | #define trans_target2initiator_initializer(member) trans_target2initiator_initializer_cb(member, NULL) | ||
42 | |||
43 | #define transport_write(id, data, length) transport_execute_transaction(id, data, length, NULL, 0) | ||
44 | #define transport_read(id, data, length) transport_execute_transaction(id, NULL, 0, data, length) | ||
45 | |||
46 | static uint8_t crc8(const void *data, size_t len) { | ||
47 | const uint8_t *p = (const uint8_t *)data; | ||
48 | uint8_t crc = 0xff; | ||
49 | size_t i, j; | ||
50 | for (i = 0; i < len; i++) { | ||
51 | crc ^= p[i]; | ||
52 | for (j = 0; j < 8; j++) { | ||
53 | if ((crc & 0x80) != 0) | ||
54 | crc = (uint8_t)((crc << 1) ^ 0x31); | ||
55 | else | ||
56 | crc <<= 1; | ||
57 | } | ||
58 | } | ||
59 | return crc; | ||
60 | } | ||
61 | |||
62 | #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
63 | // Forward-declare the RPC callback handlers | ||
64 | void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); | ||
65 | void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); | ||
66 | #endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
67 | |||
68 | //////////////////////////////////////////////////// | ||
69 | // Helpers | ||
70 | |||
71 | bool transaction_handler_master(bool okay, matrix_row_t master_matrix[], matrix_row_t slave_matrix[], const char *prefix, bool (*handler)(matrix_row_t master_matrix[], matrix_row_t slave_matrix[])) { | ||
72 | if (okay) { | ||
73 | bool this_okay = true; | ||
74 | for (int iter = 1; iter <= 10; ++iter) { | ||
75 | if (!this_okay) { | ||
76 | for (int i = 0; i < iter * iter; ++i) { | ||
77 | wait_us(10); | ||
78 | } | ||
79 | } | ||
80 | ATOMIC_BLOCK_FORCEON { this_okay = handler(master_matrix, slave_matrix); }; | ||
81 | if (this_okay) break; | ||
82 | } | ||
83 | okay &= this_okay; | ||
84 | if (!okay) { | ||
85 | dprintf("Failed to execute %s\n", prefix); | ||
86 | } | ||
87 | } | ||
88 | return okay; | ||
89 | } | ||
90 | |||
91 | #define TRANSACTION_HANDLER_MASTER(prefix) \ | ||
92 | do { \ | ||
93 | okay &= transaction_handler_master(okay, master_matrix, slave_matrix, #prefix, &prefix##_master); \ | ||
94 | } while (0) | ||
95 | |||
96 | #define TRANSACTION_HANDLER_SLAVE(prefix) \ | ||
97 | do { \ | ||
98 | ATOMIC_BLOCK_FORCEON { prefix##_slave(master_matrix, slave_matrix); }; \ | ||
99 | } while (0) | ||
100 | |||
101 | inline static bool read_if_checksum_mismatch(int8_t trans_id_checksum, int8_t trans_id_retrieve, uint32_t *last_update, void *destination, const void *equiv_shmem, size_t length) { | ||
102 | uint8_t curr_checksum; | ||
103 | bool okay = transport_read(trans_id_checksum, &curr_checksum, sizeof(curr_checksum)); | ||
104 | if (okay && (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || curr_checksum != crc8(equiv_shmem, length))) { | ||
105 | okay &= transport_read(trans_id_retrieve, destination, length); | ||
106 | okay &= curr_checksum == crc8(equiv_shmem, length); | ||
107 | if (okay) { | ||
108 | *last_update = timer_read32(); | ||
109 | } | ||
110 | } else { | ||
111 | memcpy(destination, equiv_shmem, length); | ||
112 | } | ||
113 | return okay; | ||
114 | } | ||
115 | |||
116 | inline static bool send_if_condition(int8_t trans_id, uint32_t *last_update, bool condition, void *source, size_t length) { | ||
117 | bool okay = true; | ||
118 | if (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || condition) { | ||
119 | okay &= transport_write(trans_id, source, length); | ||
120 | if (okay) { | ||
121 | *last_update = timer_read32(); | ||
122 | } | ||
123 | } | ||
124 | return okay; | ||
125 | } | ||
126 | |||
127 | inline static bool send_if_data_mismatch(int8_t trans_id, uint32_t *last_update, void *source, const void *equiv_shmem, size_t length) { | ||
128 | // Just run a memcmp to compare the source and equivalent shmem location | ||
129 | return send_if_condition(trans_id, last_update, (memcmp(source, equiv_shmem, length) != 0), source, length); | ||
130 | } | ||
131 | |||
132 | //////////////////////////////////////////////////// | ||
133 | // Slave matrix | ||
134 | |||
135 | static bool slave_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
136 | static uint32_t last_update = 0; | ||
137 | static matrix_row_t last_matrix[(MATRIX_ROWS) / 2] = {0}; // last successfully-read matrix, so we can replicate if there are checksum errors | ||
138 | matrix_row_t temp_matrix[(MATRIX_ROWS) / 2]; // holding area while we test whether or not checksum is correct | ||
139 | |||
140 | bool okay = read_if_checksum_mismatch(GET_SLAVE_MATRIX_CHECKSUM, GET_SLAVE_MATRIX_DATA, &last_update, temp_matrix, split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix)); | ||
141 | if (okay) { | ||
142 | // Checksum matches the received data, save as the last matrix state | ||
143 | memcpy(last_matrix, temp_matrix, sizeof(temp_matrix)); | ||
144 | } | ||
145 | // Copy out the last-known-good matrix state to the slave matrix | ||
146 | memcpy(slave_matrix, last_matrix, sizeof(last_matrix)); | ||
147 | return okay; | ||
148 | } | ||
149 | |||
150 | static void slave_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
151 | memcpy(split_shmem->smatrix.matrix, slave_matrix, sizeof(split_shmem->smatrix.matrix)); | ||
152 | split_shmem->smatrix.checksum = crc8(split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix)); | ||
153 | } | ||
154 | |||
155 | // clang-format off | ||
156 | #define TRANSACTIONS_SLAVE_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(slave_matrix_handlers) | ||
157 | #define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(slave_matrix_handlers) | ||
158 | #define TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS \ | ||
159 | [GET_SLAVE_MATRIX_CHECKSUM] = trans_target2initiator_initializer(smatrix.checksum), \ | ||
160 | [GET_SLAVE_MATRIX_DATA] = trans_target2initiator_initializer(smatrix.matrix), | ||
161 | // clang-format on | ||
162 | |||
163 | //////////////////////////////////////////////////// | ||
164 | // Master matrix | ||
165 | |||
166 | #ifdef SPLIT_TRANSPORT_MIRROR | ||
167 | |||
168 | static bool master_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
169 | static uint32_t last_update = 0; | ||
170 | return send_if_data_mismatch(PUT_MASTER_MATRIX, &last_update, master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix)); | ||
171 | } | ||
172 | |||
173 | static void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
174 | // Always copy to the master matrix | ||
175 | memcpy(master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix)); | ||
176 | } | ||
177 | |||
178 | # define TRANSACTIONS_MASTER_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(master_matrix_handlers) | ||
179 | # define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(master_matrix_handlers) | ||
180 | # define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS [PUT_MASTER_MATRIX] = trans_initiator2target_initializer(mmatrix.matrix), | ||
181 | |||
182 | #else // SPLIT_TRANSPORT_MIRROR | ||
183 | |||
184 | # define TRANSACTIONS_MASTER_MATRIX_MASTER() | ||
185 | # define TRANSACTIONS_MASTER_MATRIX_SLAVE() | ||
186 | # define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS | ||
187 | |||
188 | #endif // SPLIT_TRANSPORT_MIRROR | ||
189 | |||
190 | //////////////////////////////////////////////////// | ||
191 | // Encoders | ||
192 | |||
193 | #ifdef ENCODER_ENABLE | ||
194 | |||
195 | static bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
196 | static uint32_t last_update = 0; | ||
197 | uint8_t temp_state[NUMBER_OF_ENCODERS]; | ||
198 | |||
199 | bool okay = read_if_checksum_mismatch(GET_ENCODERS_CHECKSUM, GET_ENCODERS_DATA, &last_update, temp_state, split_shmem->encoders.state, sizeof(temp_state)); | ||
200 | if (okay) encoder_update_raw(temp_state); | ||
201 | return okay; | ||
202 | } | ||
203 | |||
204 | static void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
205 | uint8_t encoder_state[NUMBER_OF_ENCODERS]; | ||
206 | encoder_state_raw(encoder_state); | ||
207 | // Always prepare the encoder state for read. | ||
208 | memcpy(split_shmem->encoders.state, encoder_state, sizeof(encoder_state)); | ||
209 | // Now update the checksum given that the encoders has been written to | ||
210 | split_shmem->encoders.checksum = crc8(encoder_state, sizeof(encoder_state)); | ||
211 | } | ||
212 | |||
213 | // clang-format off | ||
214 | # define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder_handlers) | ||
215 | # define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder_handlers) | ||
216 | # define TRANSACTIONS_ENCODERS_REGISTRATIONS \ | ||
217 | [GET_ENCODERS_CHECKSUM] = trans_target2initiator_initializer(encoders.checksum), \ | ||
218 | [GET_ENCODERS_DATA] = trans_target2initiator_initializer(encoders.state), | ||
219 | // clang-format on | ||
220 | |||
221 | #else // ENCODER_ENABLE | ||
222 | |||
223 | # define TRANSACTIONS_ENCODERS_MASTER() | ||
224 | # define TRANSACTIONS_ENCODERS_SLAVE() | ||
225 | # define TRANSACTIONS_ENCODERS_REGISTRATIONS | ||
226 | |||
227 | #endif // ENCODER_ENABLE | ||
228 | |||
229 | //////////////////////////////////////////////////// | ||
230 | // Sync timer | ||
231 | |||
232 | #ifndef DISABLE_SYNC_TIMER | ||
233 | |||
234 | static bool sync_timer_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
235 | static uint32_t last_update = 0; | ||
236 | |||
237 | bool okay = true; | ||
238 | if (timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS) { | ||
239 | uint32_t sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; | ||
240 | okay &= transport_write(PUT_SYNC_TIMER, &sync_timer, sizeof(sync_timer)); | ||
241 | if (okay) { | ||
242 | last_update = timer_read32(); | ||
243 | } | ||
244 | } | ||
245 | return okay; | ||
246 | } | ||
247 | |||
248 | static void sync_timer_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
249 | static uint32_t last_sync_timer = 0; | ||
250 | if (last_sync_timer != split_shmem->sync_timer) { | ||
251 | last_sync_timer = split_shmem->sync_timer; | ||
252 | sync_timer_update(last_sync_timer); | ||
253 | } | ||
254 | } | ||
255 | |||
256 | # define TRANSACTIONS_SYNC_TIMER_MASTER() TRANSACTION_HANDLER_MASTER(sync_timer_handlers) | ||
257 | # define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE(sync_timer_handlers) | ||
258 | # define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS [PUT_SYNC_TIMER] = trans_initiator2target_initializer(sync_timer), | ||
259 | |||
260 | #else // DISABLE_SYNC_TIMER | ||
261 | |||
262 | # define TRANSACTIONS_SYNC_TIMER_MASTER() | ||
263 | # define TRANSACTIONS_SYNC_TIMER_SLAVE() | ||
264 | # define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS | ||
265 | |||
266 | #endif // DISABLE_SYNC_TIMER | ||
267 | |||
268 | //////////////////////////////////////////////////// | ||
269 | // Layer state | ||
270 | |||
271 | #if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) | ||
272 | |||
273 | static bool layer_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
274 | static uint32_t last_layer_state_update = 0; | ||
275 | static uint32_t last_default_layer_state_update = 0; | ||
276 | |||
277 | bool okay = send_if_condition(PUT_LAYER_STATE, &last_layer_state_update, (layer_state != split_shmem->layers.layer_state), &layer_state, sizeof(layer_state)); | ||
278 | if (okay) { | ||
279 | okay &= send_if_condition(PUT_DEFAULT_LAYER_STATE, &last_default_layer_state_update, (default_layer_state != split_shmem->layers.default_layer_state), &default_layer_state, sizeof(default_layer_state)); | ||
280 | } | ||
281 | return okay; | ||
282 | } | ||
283 | |||
284 | static void layer_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
285 | layer_state = split_shmem->layers.layer_state; | ||
286 | default_layer_state = split_shmem->layers.default_layer_state; | ||
287 | } | ||
288 | |||
289 | // clang-format off | ||
290 | # define TRANSACTIONS_LAYER_STATE_MASTER() TRANSACTION_HANDLER_MASTER(layer_state_handlers) | ||
291 | # define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(layer_state_handlers) | ||
292 | # define TRANSACTIONS_LAYER_STATE_REGISTRATIONS \ | ||
293 | [PUT_LAYER_STATE] = trans_initiator2target_initializer(layers.layer_state), \ | ||
294 | [PUT_DEFAULT_LAYER_STATE] = trans_initiator2target_initializer(layers.default_layer_state), | ||
295 | // clang-format on | ||
296 | |||
297 | #else // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) | ||
298 | |||
299 | # define TRANSACTIONS_LAYER_STATE_MASTER() | ||
300 | # define TRANSACTIONS_LAYER_STATE_SLAVE() | ||
301 | # define TRANSACTIONS_LAYER_STATE_REGISTRATIONS | ||
302 | |||
303 | #endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) | ||
304 | |||
305 | //////////////////////////////////////////////////// | ||
306 | // LED state | ||
307 | |||
308 | #ifdef SPLIT_LED_STATE_ENABLE | ||
309 | |||
310 | static bool led_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
311 | static uint32_t last_update = 0; | ||
312 | uint8_t led_state = host_keyboard_leds(); | ||
313 | return send_if_data_mismatch(PUT_LED_STATE, &last_update, &led_state, &split_shmem->led_state, sizeof(led_state)); | ||
314 | } | ||
315 | |||
316 | static void led_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
317 | void set_split_host_keyboard_leds(uint8_t led_state); | ||
318 | set_split_host_keyboard_leds(split_shmem->led_state); | ||
319 | } | ||
320 | |||
321 | # define TRANSACTIONS_LED_STATE_MASTER() TRANSACTION_HANDLER_MASTER(led_state_handlers) | ||
322 | # define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(led_state_handlers) | ||
323 | # define TRANSACTIONS_LED_STATE_REGISTRATIONS [PUT_LED_STATE] = trans_initiator2target_initializer(led_state), | ||
324 | |||
325 | #else // SPLIT_LED_STATE_ENABLE | ||
326 | |||
327 | # define TRANSACTIONS_LED_STATE_MASTER() | ||
328 | # define TRANSACTIONS_LED_STATE_SLAVE() | ||
329 | # define TRANSACTIONS_LED_STATE_REGISTRATIONS | ||
330 | |||
331 | #endif // SPLIT_LED_STATE_ENABLE | ||
332 | |||
333 | //////////////////////////////////////////////////// | ||
334 | // Mods | ||
335 | |||
336 | #ifdef SPLIT_MODS_ENABLE | ||
337 | |||
338 | static bool mods_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
339 | static uint32_t last_update = 0; | ||
340 | bool mods_need_sync = timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS; | ||
341 | split_mods_sync_t new_mods; | ||
342 | new_mods.real_mods = get_mods(); | ||
343 | if (!mods_need_sync && new_mods.real_mods != split_shmem->mods.real_mods) { | ||
344 | mods_need_sync = true; | ||
345 | } | ||
346 | |||
347 | new_mods.weak_mods = get_weak_mods(); | ||
348 | if (!mods_need_sync && new_mods.weak_mods != split_shmem->mods.weak_mods) { | ||
349 | mods_need_sync = true; | ||
350 | } | ||
351 | |||
352 | # ifndef NO_ACTION_ONESHOT | ||
353 | new_mods.oneshot_mods = get_oneshot_mods(); | ||
354 | if (!mods_need_sync && new_mods.oneshot_mods != split_shmem->mods.oneshot_mods) { | ||
355 | mods_need_sync = true; | ||
356 | } | ||
357 | # endif // NO_ACTION_ONESHOT | ||
358 | |||
359 | bool okay = true; | ||
360 | if (mods_need_sync) { | ||
361 | okay &= transport_write(PUT_MODS, &new_mods, sizeof(new_mods)); | ||
362 | if (okay) { | ||
363 | last_update = timer_read32(); | ||
364 | } | ||
365 | } | ||
366 | |||
367 | return okay; | ||
368 | } | ||
369 | |||
370 | static void mods_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
371 | set_mods(split_shmem->mods.real_mods); | ||
372 | set_weak_mods(split_shmem->mods.weak_mods); | ||
373 | # ifndef NO_ACTION_ONESHOT | ||
374 | set_oneshot_mods(split_shmem->mods.oneshot_mods); | ||
375 | # endif | ||
376 | } | ||
377 | |||
378 | # define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods_handlers) | ||
379 | # define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods_handlers) | ||
380 | # define TRANSACTIONS_MODS_REGISTRATIONS [PUT_MODS] = trans_initiator2target_initializer(mods), | ||
381 | |||
382 | #else // SPLIT_MODS_ENABLE | ||
383 | |||
384 | # define TRANSACTIONS_MODS_MASTER() | ||
385 | # define TRANSACTIONS_MODS_SLAVE() | ||
386 | # define TRANSACTIONS_MODS_REGISTRATIONS | ||
387 | |||
388 | #endif // SPLIT_MODS_ENABLE | ||
389 | |||
390 | //////////////////////////////////////////////////// | ||
391 | // Backlight | ||
392 | |||
393 | #ifdef BACKLIGHT_ENABLE | ||
394 | |||
395 | static bool backlight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
396 | static uint32_t last_update = 0; | ||
397 | uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0; | ||
398 | return send_if_condition(PUT_BACKLIGHT, &last_update, (level != split_shmem->backlight_level), &level, sizeof(level)); | ||
399 | } | ||
400 | |||
401 | static void backlight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { backlight_set(split_shmem->backlight_level); } | ||
402 | |||
403 | # define TRANSACTIONS_BACKLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(backlight_handlers) | ||
404 | # define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight_handlers) | ||
405 | # define TRANSACTIONS_BACKLIGHT_REGISTRATIONS [PUT_BACKLIGHT] = trans_initiator2target_initializer(backlight_level), | ||
406 | |||
407 | #else // BACKLIGHT_ENABLE | ||
408 | |||
409 | # define TRANSACTIONS_BACKLIGHT_MASTER() | ||
410 | # define TRANSACTIONS_BACKLIGHT_SLAVE() | ||
411 | # define TRANSACTIONS_BACKLIGHT_REGISTRATIONS | ||
412 | |||
413 | #endif // BACKLIGHT_ENABLE | ||
414 | |||
415 | //////////////////////////////////////////////////// | ||
416 | // RGBLIGHT | ||
417 | |||
418 | #if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
419 | |||
420 | static bool rgblight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
421 | static uint32_t last_update = 0; | ||
422 | rgblight_syncinfo_t rgblight_sync; | ||
423 | rgblight_get_syncinfo(&rgblight_sync); | ||
424 | if (send_if_condition(PUT_RGBLIGHT, &last_update, (rgblight_sync.status.change_flags != 0), &rgblight_sync, sizeof(rgblight_sync))) { | ||
425 | rgblight_clear_change_flags(); | ||
426 | } else { | ||
427 | return false; | ||
428 | } | ||
429 | return true; | ||
430 | } | ||
431 | |||
432 | static void rgblight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
433 | // Update the RGB with the new data | ||
434 | if (split_shmem->rgblight_sync.status.change_flags != 0) { | ||
435 | rgblight_update_sync(&split_shmem->rgblight_sync, false); | ||
436 | split_shmem->rgblight_sync.status.change_flags = 0; | ||
437 | } | ||
438 | } | ||
439 | |||
440 | # define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight_handlers) | ||
441 | # define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight_handlers) | ||
442 | # define TRANSACTIONS_RGBLIGHT_REGISTRATIONS [PUT_RGBLIGHT] = trans_initiator2target_initializer(rgblight_sync), | ||
443 | |||
444 | #else // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
445 | |||
446 | # define TRANSACTIONS_RGBLIGHT_MASTER() | ||
447 | # define TRANSACTIONS_RGBLIGHT_SLAVE() | ||
448 | # define TRANSACTIONS_RGBLIGHT_REGISTRATIONS | ||
449 | |||
450 | #endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
451 | |||
452 | //////////////////////////////////////////////////// | ||
453 | // LED Matrix | ||
454 | |||
455 | #if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
456 | |||
457 | static bool led_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
458 | static uint32_t last_update = 0; | ||
459 | led_matrix_sync_t led_matrix_sync; | ||
460 | memcpy(&led_matrix_sync.led_matrix, &led_matrix_eeconfig, sizeof(led_eeconfig_t)); | ||
461 | led_matrix_sync.led_suspend_state = led_matrix_get_suspend_state(); | ||
462 | return send_if_data_mismatch(PUT_LED_MATRIX, &last_update, &led_matrix_sync, &split_shmem->led_matrix_sync, sizeof(led_matrix_sync)); | ||
463 | } | ||
464 | |||
465 | static void led_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
466 | memcpy(&led_matrix_eeconfig, &split_shmem->led_matrix_sync.led_matrix, sizeof(led_eeconfig_t)); | ||
467 | led_matrix_set_suspend_state(split_shmem->led_matrix_sync.led_suspend_state); | ||
468 | } | ||
469 | |||
470 | # define TRANSACTIONS_LED_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(led_matrix_handlers) | ||
471 | # define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix_handlers) | ||
472 | # define TRANSACTIONS_LED_MATRIX_REGISTRATIONS [PUT_LED_MATRIX] = trans_initiator2target_initializer(led_matrix_sync), | ||
473 | |||
474 | #else // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
475 | |||
476 | # define TRANSACTIONS_LED_MATRIX_MASTER() | ||
477 | # define TRANSACTIONS_LED_MATRIX_SLAVE() | ||
478 | # define TRANSACTIONS_LED_MATRIX_REGISTRATIONS | ||
479 | |||
480 | #endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
481 | |||
482 | //////////////////////////////////////////////////// | ||
483 | // RGB Matrix | ||
484 | |||
485 | #if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
486 | |||
487 | static bool rgb_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
488 | static uint32_t last_update = 0; | ||
489 | rgb_matrix_sync_t rgb_matrix_sync; | ||
490 | memcpy(&rgb_matrix_sync.rgb_matrix, &rgb_matrix_config, sizeof(rgb_config_t)); | ||
491 | rgb_matrix_sync.rgb_suspend_state = rgb_matrix_get_suspend_state(); | ||
492 | return send_if_data_mismatch(PUT_RGB_MATRIX, &last_update, &rgb_matrix_sync, &split_shmem->rgb_matrix_sync, sizeof(rgb_matrix_sync)); | ||
493 | } | ||
494 | |||
495 | static void rgb_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
496 | memcpy(&rgb_matrix_config, &split_shmem->rgb_matrix_sync.rgb_matrix, sizeof(rgb_config_t)); | ||
497 | rgb_matrix_set_suspend_state(split_shmem->rgb_matrix_sync.rgb_suspend_state); | ||
498 | } | ||
499 | |||
500 | # define TRANSACTIONS_RGB_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(rgb_matrix_handlers) | ||
501 | # define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix_handlers) | ||
502 | # define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS [PUT_RGB_MATRIX] = trans_initiator2target_initializer(rgb_matrix_sync), | ||
503 | |||
504 | #else // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
505 | |||
506 | # define TRANSACTIONS_RGB_MATRIX_MASTER() | ||
507 | # define TRANSACTIONS_RGB_MATRIX_SLAVE() | ||
508 | # define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS | ||
509 | |||
510 | #endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
511 | |||
512 | //////////////////////////////////////////////////// | ||
513 | // WPM | ||
514 | |||
515 | #if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) | ||
516 | |||
517 | static bool wpm_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
518 | static uint32_t last_update = 0; | ||
519 | uint8_t current_wpm = get_current_wpm(); | ||
520 | return send_if_condition(PUT_WPM, &last_update, (current_wpm != split_shmem->current_wpm), ¤t_wpm, sizeof(current_wpm)); | ||
521 | } | ||
522 | |||
523 | static void wpm_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { set_current_wpm(split_shmem->current_wpm); } | ||
524 | |||
525 | # define TRANSACTIONS_WPM_MASTER() TRANSACTION_HANDLER_MASTER(wpm_handlers) | ||
526 | # define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE(wpm_handlers) | ||
527 | # define TRANSACTIONS_WPM_REGISTRATIONS [PUT_WPM] = trans_initiator2target_initializer(current_wpm), | ||
528 | |||
529 | #else // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) | ||
530 | |||
531 | # define TRANSACTIONS_WPM_MASTER() | ||
532 | # define TRANSACTIONS_WPM_SLAVE() | ||
533 | # define TRANSACTIONS_WPM_REGISTRATIONS | ||
534 | |||
535 | #endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) | ||
536 | |||
537 | //////////////////////////////////////////////////// | ||
538 | |||
539 | uint8_t dummy; | ||
540 | split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = { | ||
541 | // Set defaults | ||
542 | [0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {NULL, 0, 0, 0, 0, 0}, | ||
543 | |||
544 | #ifdef USE_I2C | ||
545 | [I2C_EXECUTE_CALLBACK] = trans_initiator2target_initializer(transaction_id), | ||
546 | #endif // USE_I2C | ||
547 | |||
548 | // clang-format off | ||
549 | TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS | ||
550 | TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS | ||
551 | TRANSACTIONS_ENCODERS_REGISTRATIONS | ||
552 | TRANSACTIONS_SYNC_TIMER_REGISTRATIONS | ||
553 | TRANSACTIONS_LAYER_STATE_REGISTRATIONS | ||
554 | TRANSACTIONS_LED_STATE_REGISTRATIONS | ||
555 | TRANSACTIONS_MODS_REGISTRATIONS | ||
556 | TRANSACTIONS_BACKLIGHT_REGISTRATIONS | ||
557 | TRANSACTIONS_RGBLIGHT_REGISTRATIONS | ||
558 | TRANSACTIONS_LED_MATRIX_REGISTRATIONS | ||
559 | TRANSACTIONS_RGB_MATRIX_REGISTRATIONS | ||
560 | TRANSACTIONS_WPM_REGISTRATIONS | ||
561 | // clang-format on | ||
562 | |||
563 | #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
564 | [PUT_RPC_INFO] = trans_initiator2target_initializer_cb(rpc_info, slave_rpc_info_callback), | ||
565 | [PUT_RPC_REQ_DATA] = trans_initiator2target_initializer(rpc_m2s_buffer), | ||
566 | [EXECUTE_RPC] = trans_initiator2target_initializer_cb(rpc_info.transaction_id, slave_rpc_exec_callback), | ||
567 | [GET_RPC_RESP_DATA] = trans_target2initiator_initializer(rpc_s2m_buffer), | ||
568 | #endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
569 | }; | ||
570 | |||
571 | bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
572 | bool okay = true; | ||
573 | TRANSACTIONS_SLAVE_MATRIX_MASTER(); | ||
574 | TRANSACTIONS_MASTER_MATRIX_MASTER(); | ||
575 | TRANSACTIONS_ENCODERS_MASTER(); | ||
576 | TRANSACTIONS_SYNC_TIMER_MASTER(); | ||
577 | TRANSACTIONS_LAYER_STATE_MASTER(); | ||
578 | TRANSACTIONS_LED_STATE_MASTER(); | ||
579 | TRANSACTIONS_MODS_MASTER(); | ||
580 | TRANSACTIONS_BACKLIGHT_MASTER(); | ||
581 | TRANSACTIONS_RGBLIGHT_MASTER(); | ||
582 | TRANSACTIONS_LED_MATRIX_MASTER(); | ||
583 | TRANSACTIONS_RGB_MATRIX_MASTER(); | ||
584 | TRANSACTIONS_WPM_MASTER(); | ||
585 | return okay; | ||
586 | } | ||
587 | |||
588 | void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
589 | TRANSACTIONS_SLAVE_MATRIX_SLAVE(); | ||
590 | TRANSACTIONS_MASTER_MATRIX_SLAVE(); | ||
591 | TRANSACTIONS_ENCODERS_SLAVE(); | ||
592 | TRANSACTIONS_SYNC_TIMER_SLAVE(); | ||
593 | TRANSACTIONS_LAYER_STATE_SLAVE(); | ||
594 | TRANSACTIONS_LED_STATE_SLAVE(); | ||
595 | TRANSACTIONS_MODS_SLAVE(); | ||
596 | TRANSACTIONS_BACKLIGHT_SLAVE(); | ||
597 | TRANSACTIONS_RGBLIGHT_SLAVE(); | ||
598 | TRANSACTIONS_LED_MATRIX_SLAVE(); | ||
599 | TRANSACTIONS_RGB_MATRIX_SLAVE(); | ||
600 | TRANSACTIONS_WPM_SLAVE(); | ||
601 | } | ||
602 | |||
603 | #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
604 | |||
605 | void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback) { | ||
606 | // Prevent invoking RPC on QMK core sync data | ||
607 | if (transaction_id <= GET_RPC_RESP_DATA) return; | ||
608 | |||
609 | // Set the callback | ||
610 | split_transaction_table[transaction_id].slave_callback = callback; | ||
611 | split_transaction_table[transaction_id].initiator2target_offset = offsetof(split_shared_memory_t, rpc_m2s_buffer); | ||
612 | split_transaction_table[transaction_id].target2initiator_offset = offsetof(split_shared_memory_t, rpc_s2m_buffer); | ||
613 | } | ||
614 | |||
615 | bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) { | ||
616 | // Prevent invoking RPC on QMK core sync data | ||
617 | if (transaction_id <= GET_RPC_RESP_DATA) return false; | ||
618 | // Prevent sizing issues | ||
619 | if (initiator2target_buffer_size > RPC_M2S_BUFFER_SIZE) return false; | ||
620 | if (target2initiator_buffer_size > RPC_S2M_BUFFER_SIZE) return false; | ||
621 | |||
622 | // Prepare the metadata block | ||
623 | rpc_sync_info_t info = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size}; | ||
624 | |||
625 | // Make sure the local side knows that we're not sending the full block of data | ||
626 | split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = initiator2target_buffer_size; | ||
627 | split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = target2initiator_buffer_size; | ||
628 | |||
629 | // Run through the sequence: | ||
630 | // * set the transaction ID and lengths | ||
631 | // * send the request data | ||
632 | // * execute RPC callback | ||
633 | // * retrieve the response data | ||
634 | if (!transport_write(PUT_RPC_INFO, &info, sizeof(info))) { | ||
635 | return false; | ||
636 | } | ||
637 | if (!transport_write(PUT_RPC_REQ_DATA, initiator2target_buffer, initiator2target_buffer_size)) { | ||
638 | return false; | ||
639 | } | ||
640 | if (!transport_write(EXECUTE_RPC, &transaction_id, sizeof(transaction_id))) { | ||
641 | return false; | ||
642 | } | ||
643 | if (!transport_read(GET_RPC_RESP_DATA, target2initiator_buffer, target2initiator_buffer_size)) { | ||
644 | return false; | ||
645 | } | ||
646 | return true; | ||
647 | } | ||
648 | |||
649 | void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) { | ||
650 | // The RPC info block contains the intended transaction ID, as well as the sizes for both inbound and outbound data. | ||
651 | // Ignore the args -- the `split_shmem` already has the info, we just need to act upon it. | ||
652 | // We must keep the `split_transaction_table` non-const, so that it is able to be modified at runtime. | ||
653 | |||
654 | split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = split_shmem->rpc_info.m2s_length; | ||
655 | split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.s2m_length; | ||
656 | } | ||
657 | |||
658 | void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) { | ||
659 | // We can assume that the buffer lengths are correctly set, now, given that sequentially the rpc_info callback was already executed. | ||
660 | // Go through the rpc_info and execute _that_ transaction's callback, with the scratch buffers as inputs. | ||
661 | int8_t transaction_id = split_shmem->rpc_info.transaction_id; | ||
662 | if (transaction_id < NUM_TOTAL_TRANSACTIONS) { | ||
663 | split_transaction_desc_t *trans = &split_transaction_table[transaction_id]; | ||
664 | if (trans->slave_callback) { | ||
665 | trans->slave_callback(split_shmem->rpc_info.m2s_length, split_shmem->rpc_m2s_buffer, split_shmem->rpc_info.s2m_length, split_shmem->rpc_s2m_buffer); | ||
666 | } | ||
667 | } | ||
668 | } | ||
669 | |||
670 | #endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
diff --git a/quantum/split_common/transactions.h b/quantum/split_common/transactions.h new file mode 100644 index 000000000..4306ba1d8 --- /dev/null +++ b/quantum/split_common/transactions.h | |||
@@ -0,0 +1,54 @@ | |||
1 | /* Copyright 2021 QMK | ||
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 | #pragma once | ||
18 | |||
19 | #include "stdint.h" | ||
20 | #include "stdbool.h" | ||
21 | |||
22 | #include "matrix.h" | ||
23 | #include "transaction_id_define.h" | ||
24 | #include "transport.h" | ||
25 | |||
26 | typedef void (*slave_callback_t)(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); | ||
27 | |||
28 | // Split transaction Descriptor | ||
29 | typedef struct _split_transaction_desc_t { | ||
30 | uint8_t * status; | ||
31 | uint8_t initiator2target_buffer_size; | ||
32 | uint16_t initiator2target_offset; | ||
33 | uint8_t target2initiator_buffer_size; | ||
34 | uint16_t target2initiator_offset; | ||
35 | slave_callback_t slave_callback; | ||
36 | } split_transaction_desc_t; | ||
37 | |||
38 | // Forward declaration for the split transactions | ||
39 | extern split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS]; | ||
40 | |||
41 | #define split_shmem_offset_ptr(offset) ((void *)(((uint8_t *)split_shmem) + (offset))) | ||
42 | #define split_trans_initiator2target_buffer(trans) (split_shmem_offset_ptr((trans)->initiator2target_offset)) | ||
43 | #define split_trans_target2initiator_buffer(trans) (split_shmem_offset_ptr((trans)->target2initiator_offset)) | ||
44 | |||
45 | // returns false if valid data not received from slave | ||
46 | bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); | ||
47 | void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); | ||
48 | |||
49 | void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback); | ||
50 | |||
51 | bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); | ||
52 | |||
53 | #define transaction_rpc_send(transaction_id, initiator2target_buffer_size, initiator2target_buffer) transaction_rpc_exec(transaction_id, initiator2target_buffer_size, initiator2target_buffer, 0, NULL) | ||
54 | #define transaction_rpc_recv(transaction_id, target2initiator_buffer_size, target2initiator_buffer) transaction_rpc_exec(transaction_id, 0, NULL, target2initiator_buffer_size, target2initiator_buffer) | ||
diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c index 9ed0f7591..a711ef85f 100644 --- a/quantum/split_common/transport.c +++ b/quantum/split_common/transport.c | |||
@@ -1,452 +1,118 @@ | |||
1 | #include <string.h> | 1 | /* Copyright 2021 QMK |
2 | #include <stddef.h> | 2 | * |
3 | 3 | * This program is free software: you can redistribute it and/or modify | |
4 | #include "config.h" | 4 | * it under the terms of the GNU General Public License as published by |
5 | #include "matrix.h" | 5 | * the Free Software Foundation, either version 2 of the License, or |
6 | #include "quantum.h" | 6 | * (at your option) any later version. |
7 | 7 | * | |
8 | #define ROWS_PER_HAND (MATRIX_ROWS / 2) | 8 | * This program is distributed in the hope that it will be useful, |
9 | #define SYNC_TIMER_OFFSET 2 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | #ifdef RGBLIGHT_ENABLE | 11 | * GNU General Public License for more details. |
12 | # include "rgblight.h" | 12 | * |
13 | #endif | 13 | * You should have received a copy of the GNU General Public License |
14 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | #ifdef BACKLIGHT_ENABLE | 15 | */ |
16 | # include "backlight.h" | ||
17 | #endif | ||
18 | |||
19 | #ifdef ENCODER_ENABLE | ||
20 | # include "encoder.h" | ||
21 | static pin_t encoders_pad[] = ENCODERS_PAD_A; | ||
22 | # define NUMBER_OF_ENCODERS (sizeof(encoders_pad) / sizeof(pin_t)) | ||
23 | #endif | ||
24 | |||
25 | #if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
26 | # include "led_matrix.h" | ||
27 | #endif | ||
28 | #if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
29 | # include "rgb_matrix.h" | ||
30 | #endif | ||
31 | |||
32 | #if defined(USE_I2C) | ||
33 | 16 | ||
34 | # include "i2c_master.h" | 17 | #include <string.h> |
35 | # include "i2c_slave.h" | 18 | #include <debug.h> |
36 | |||
37 | typedef struct _I2C_slave_buffer_t { | ||
38 | # ifndef DISABLE_SYNC_TIMER | ||
39 | uint32_t sync_timer; | ||
40 | # endif | ||
41 | # ifdef SPLIT_TRANSPORT_MIRROR | ||
42 | matrix_row_t mmatrix[ROWS_PER_HAND]; | ||
43 | # endif | ||
44 | matrix_row_t smatrix[ROWS_PER_HAND]; | ||
45 | # ifdef SPLIT_MODS_ENABLE | ||
46 | uint8_t real_mods; | ||
47 | uint8_t weak_mods; | ||
48 | # ifndef NO_ACTION_ONESHOT | ||
49 | uint8_t oneshot_mods; | ||
50 | # endif | ||
51 | # endif | ||
52 | # ifdef BACKLIGHT_ENABLE | ||
53 | uint8_t backlight_level; | ||
54 | # endif | ||
55 | # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
56 | rgblight_syncinfo_t rgblight_sync; | ||
57 | # endif | ||
58 | # ifdef ENCODER_ENABLE | ||
59 | uint8_t encoder_state[NUMBER_OF_ENCODERS]; | ||
60 | # endif | ||
61 | # ifdef WPM_ENABLE | ||
62 | uint8_t current_wpm; | ||
63 | # endif | ||
64 | # if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
65 | led_eeconfig_t led_matrix; | ||
66 | bool led_suspend_state; | ||
67 | # endif | ||
68 | # if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
69 | rgb_config_t rgb_matrix; | ||
70 | bool rgb_suspend_state; | ||
71 | # endif | ||
72 | } I2C_slave_buffer_t; | ||
73 | 19 | ||
74 | static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg; | 20 | #include "transactions.h" |
21 | #include "transport.h" | ||
22 | #include "transaction_id_define.h" | ||
23 | #include "atomic_util.h" | ||
75 | 24 | ||
76 | # define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer) | 25 | #ifdef USE_I2C |
77 | # define I2C_KEYMAP_MASTER_START offsetof(I2C_slave_buffer_t, mmatrix) | ||
78 | # define I2C_KEYMAP_SLAVE_START offsetof(I2C_slave_buffer_t, smatrix) | ||
79 | # define I2C_REAL_MODS_START offsetof(I2C_slave_buffer_t, real_mods) | ||
80 | # define I2C_WEAK_MODS_START offsetof(I2C_slave_buffer_t, weak_mods) | ||
81 | # define I2C_ONESHOT_MODS_START offsetof(I2C_slave_buffer_t, oneshot_mods) | ||
82 | # define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level) | ||
83 | # define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync) | ||
84 | # define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state) | ||
85 | # define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm) | ||
86 | # define I2C_LED_MATRIX_START offsetof(I2C_slave_buffer_t, led_matrix) | ||
87 | # define I2C_LED_SUSPEND_START offsetof(I2C_slave_buffer_t, led_suspend_state) | ||
88 | # define I2C_RGB_MATRIX_START offsetof(I2C_slave_buffer_t, rgb_matrix) | ||
89 | # define I2C_RGB_SUSPEND_START offsetof(I2C_slave_buffer_t, rgb_suspend_state) | ||
90 | 26 | ||
91 | # define TIMEOUT 100 | 27 | # ifndef SLAVE_I2C_TIMEOUT |
28 | # define SLAVE_I2C_TIMEOUT 100 | ||
29 | # endif // SLAVE_I2C_TIMEOUT | ||
92 | 30 | ||
93 | # ifndef SLAVE_I2C_ADDRESS | 31 | # ifndef SLAVE_I2C_ADDRESS |
94 | # define SLAVE_I2C_ADDRESS 0x32 | 32 | # define SLAVE_I2C_ADDRESS 0x32 |
95 | # endif | 33 | # endif |
96 | 34 | ||
97 | // Get rows from other half over i2c | 35 | # include "i2c_master.h" |
98 | bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | 36 | # include "i2c_slave.h" |
99 | i2c_readReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_SLAVE_START, (void *)slave_matrix, sizeof(i2c_buffer->smatrix), TIMEOUT); | ||
100 | # ifdef SPLIT_TRANSPORT_MIRROR | ||
101 | i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_MASTER_START, (void *)master_matrix, sizeof(i2c_buffer->mmatrix), TIMEOUT); | ||
102 | # endif | ||
103 | 37 | ||
104 | // write backlight info | 38 | // Ensure the I2C buffer has enough space |
105 | # ifdef BACKLIGHT_ENABLE | 39 | _Static_assert(sizeof(split_shared_memory_t) <= I2C_SLAVE_REG_COUNT, "split_shared_memory_t too large for I2C_SLAVE_REG_COUNT"); |
106 | uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0; | ||
107 | if (level != i2c_buffer->backlight_level) { | ||
108 | if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_BACKLIGHT_START, (void *)&level, sizeof(level), TIMEOUT) >= 0) { | ||
109 | i2c_buffer->backlight_level = level; | ||
110 | } | ||
111 | } | ||
112 | # endif | ||
113 | 40 | ||
114 | # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | 41 | split_shared_memory_t *const split_shmem = (split_shared_memory_t *)i2c_slave_reg; |
115 | if (rgblight_get_change_flags()) { | ||
116 | rgblight_syncinfo_t rgblight_sync; | ||
117 | rgblight_get_syncinfo(&rgblight_sync); | ||
118 | if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_START, (void *)&rgblight_sync, sizeof(rgblight_sync), TIMEOUT) >= 0) { | ||
119 | rgblight_clear_change_flags(); | ||
120 | } | ||
121 | } | ||
122 | # endif | ||
123 | 42 | ||
124 | # ifdef ENCODER_ENABLE | 43 | void transport_master_init(void) { i2c_init(); } |
125 | i2c_readReg(SLAVE_I2C_ADDRESS, I2C_ENCODER_START, (void *)i2c_buffer->encoder_state, sizeof(i2c_buffer->encoder_state), TIMEOUT); | 44 | void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); } |
126 | encoder_update_raw(i2c_buffer->encoder_state); | ||
127 | # endif | ||
128 | 45 | ||
129 | # ifdef WPM_ENABLE | 46 | i2c_status_t transport_trigger_callback(int8_t id) { |
130 | uint8_t current_wpm = get_current_wpm(); | 47 | // If there's no callback, indicate that we were successful |
131 | if (current_wpm != i2c_buffer->current_wpm) { | 48 | if (!split_transaction_table[id].slave_callback) { |
132 | if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WPM_START, (void *)¤t_wpm, sizeof(current_wpm), TIMEOUT) >= 0) { | 49 | return I2C_STATUS_SUCCESS; |
133 | i2c_buffer->current_wpm = current_wpm; | ||
134 | } | ||
135 | } | 50 | } |
136 | # endif | ||
137 | 51 | ||
138 | # ifdef SPLIT_MODS_ENABLE | 52 | // Kick off the "callback executor", now that data has been written to the slave |
139 | uint8_t real_mods = get_mods(); | 53 | split_shmem->transaction_id = id; |
140 | if (real_mods != i2c_buffer->real_mods) { | 54 | split_transaction_desc_t *trans = &split_transaction_table[I2C_EXECUTE_CALLBACK]; |
141 | if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_REAL_MODS_START, (void *)&real_mods, sizeof(real_mods), TIMEOUT) >= 0) { | 55 | return i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, SLAVE_I2C_TIMEOUT); |
142 | i2c_buffer->real_mods = real_mods; | 56 | } |
57 | |||
58 | bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) { | ||
59 | i2c_status_t status; | ||
60 | split_transaction_desc_t *trans = &split_transaction_table[id]; | ||
61 | if (initiator2target_length > 0) { | ||
62 | size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length; | ||
63 | memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len); | ||
64 | if ((status = i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) { | ||
65 | return false; | ||
143 | } | 66 | } |
144 | } | 67 | } |
145 | 68 | ||
146 | uint8_t weak_mods = get_weak_mods(); | 69 | // If we need to execute a callback on the slave, do so |
147 | if (weak_mods != i2c_buffer->weak_mods) { | 70 | if ((status = transport_trigger_callback(id)) < 0) { |
148 | if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WEAK_MODS_START, (void *)&weak_mods, sizeof(weak_mods), TIMEOUT) >= 0) { | 71 | return false; |
149 | i2c_buffer->weak_mods = weak_mods; | ||
150 | } | ||
151 | } | 72 | } |
152 | 73 | ||
153 | # ifndef NO_ACTION_ONESHOT | 74 | if (target2initiator_length > 0) { |
154 | uint8_t oneshot_mods = get_oneshot_mods(); | 75 | size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length; |
155 | if (oneshot_mods != i2c_buffer->oneshot_mods) { | 76 | if ((status = i2c_readReg(SLAVE_I2C_ADDRESS, trans->target2initiator_offset, split_trans_target2initiator_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) { |
156 | if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_ONESHOT_MODS_START, (void *)&oneshot_mods, sizeof(oneshot_mods), TIMEOUT) >= 0) { | 77 | return false; |
157 | i2c_buffer->oneshot_mods = oneshot_mods; | ||
158 | } | 78 | } |
79 | memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len); | ||
159 | } | 80 | } |
160 | # endif | ||
161 | # endif | ||
162 | 81 | ||
163 | # if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
164 | i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_MATRIX_START, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix), TIMEOUT); | ||
165 | bool suspend_state = led_matrix_get_suspend_state(); | ||
166 | i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->led_suspend_state), TIMEOUT); | ||
167 | # endif | ||
168 | # if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
169 | i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_MATRIX_START, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix), TIMEOUT); | ||
170 | bool suspend_state = rgb_matrix_get_suspend_state(); | ||
171 | i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->rgb_suspend_state), TIMEOUT); | ||
172 | # endif | ||
173 | |||
174 | # ifndef DISABLE_SYNC_TIMER | ||
175 | i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; | ||
176 | i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT); | ||
177 | # endif | ||
178 | return true; | 82 | return true; |
179 | } | 83 | } |
180 | 84 | ||
181 | void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | 85 | #else // USE_I2C |
182 | # ifndef DISABLE_SYNC_TIMER | ||
183 | sync_timer_update(i2c_buffer->sync_timer); | ||
184 | # endif | ||
185 | // Copy matrix to I2C buffer | ||
186 | memcpy((void *)i2c_buffer->smatrix, (void *)slave_matrix, sizeof(i2c_buffer->smatrix)); | ||
187 | # ifdef SPLIT_TRANSPORT_MIRROR | ||
188 | memcpy((void *)master_matrix, (void *)i2c_buffer->mmatrix, sizeof(i2c_buffer->mmatrix)); | ||
189 | # endif | ||
190 | |||
191 | // Read Backlight Info | ||
192 | # ifdef BACKLIGHT_ENABLE | ||
193 | backlight_set(i2c_buffer->backlight_level); | ||
194 | # endif | ||
195 | |||
196 | # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
197 | // Update the RGB with the new data | ||
198 | if (i2c_buffer->rgblight_sync.status.change_flags != 0) { | ||
199 | rgblight_update_sync(&i2c_buffer->rgblight_sync, false); | ||
200 | i2c_buffer->rgblight_sync.status.change_flags = 0; | ||
201 | } | ||
202 | # endif | ||
203 | |||
204 | # ifdef ENCODER_ENABLE | ||
205 | encoder_state_raw(i2c_buffer->encoder_state); | ||
206 | # endif | ||
207 | |||
208 | # ifdef WPM_ENABLE | ||
209 | set_current_wpm(i2c_buffer->current_wpm); | ||
210 | # endif | ||
211 | |||
212 | # ifdef SPLIT_MODS_ENABLE | ||
213 | set_mods(i2c_buffer->real_mods); | ||
214 | set_weak_mods(i2c_buffer->weak_mods); | ||
215 | # ifndef NO_ACTION_ONESHOT | ||
216 | set_oneshot_mods(i2c_buffer->oneshot_mods); | ||
217 | # endif | ||
218 | # endif | ||
219 | |||
220 | # if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
221 | memcpy((void *)i2c_buffer->led_matrix, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix)); | ||
222 | led_matrix_set_suspend_state(i2c_buffer->led_suspend_state); | ||
223 | # endif | ||
224 | # if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
225 | memcpy((void *)i2c_buffer->rgb_matrix, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix)); | ||
226 | rgb_matrix_set_suspend_state(i2c_buffer->rgb_suspend_state); | ||
227 | # endif | ||
228 | } | ||
229 | |||
230 | void transport_master_init(void) { i2c_init(); } | ||
231 | |||
232 | void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); } | ||
233 | |||
234 | #else // USE_SERIAL | ||
235 | 86 | ||
236 | # include "serial.h" | 87 | # include "serial.h" |
237 | 88 | ||
238 | typedef struct _Serial_s2m_buffer_t { | 89 | static split_shared_memory_t shared_memory; |
239 | // TODO: if MATRIX_COLS > 8 change to uint8_t packed_matrix[] for pack/unpack | 90 | split_shared_memory_t *const split_shmem = &shared_memory; |
240 | matrix_row_t smatrix[ROWS_PER_HAND]; | ||
241 | |||
242 | # ifdef ENCODER_ENABLE | ||
243 | uint8_t encoder_state[NUMBER_OF_ENCODERS]; | ||
244 | # endif | ||
245 | |||
246 | } Serial_s2m_buffer_t; | ||
247 | |||
248 | typedef struct _Serial_m2s_buffer_t { | ||
249 | # ifdef SPLIT_MODS_ENABLE | ||
250 | uint8_t real_mods; | ||
251 | uint8_t weak_mods; | ||
252 | # ifndef NO_ACTION_ONESHOT | ||
253 | uint8_t oneshot_mods; | ||
254 | # endif | ||
255 | # endif | ||
256 | # ifndef DISABLE_SYNC_TIMER | ||
257 | uint32_t sync_timer; | ||
258 | # endif | ||
259 | # ifdef SPLIT_TRANSPORT_MIRROR | ||
260 | matrix_row_t mmatrix[ROWS_PER_HAND]; | ||
261 | # endif | ||
262 | # ifdef BACKLIGHT_ENABLE | ||
263 | uint8_t backlight_level; | ||
264 | # endif | ||
265 | # ifdef WPM_ENABLE | ||
266 | uint8_t current_wpm; | ||
267 | # endif | ||
268 | # if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
269 | led_eeconfig_t led_matrix; | ||
270 | bool led_suspend_state; | ||
271 | # endif | ||
272 | # if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
273 | rgb_config_t rgb_matrix; | ||
274 | bool rgb_suspend_state; | ||
275 | # endif | ||
276 | } Serial_m2s_buffer_t; | ||
277 | |||
278 | # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
279 | // When MCUs on both sides drive their respective RGB LED chains, | ||
280 | // it is necessary to synchronize, so it is necessary to communicate RGB | ||
281 | // information. In that case, define RGBLIGHT_SPLIT with info on the number | ||
282 | // of LEDs on each half. | ||
283 | // | ||
284 | // Otherwise, if the master side MCU drives both sides RGB LED chains, | ||
285 | // there is no need to communicate. | ||
286 | |||
287 | typedef struct _Serial_rgblight_t { | ||
288 | rgblight_syncinfo_t rgblight_sync; | ||
289 | } Serial_rgblight_t; | ||
290 | 91 | ||
291 | volatile Serial_rgblight_t serial_rgblight = {}; | 92 | void transport_master_init(void) { soft_serial_initiator_init(); } |
292 | uint8_t volatile status_rgblight = 0; | 93 | void transport_slave_init(void) { soft_serial_target_init(); } |
293 | # endif | ||
294 | |||
295 | volatile Serial_s2m_buffer_t serial_s2m_buffer = {}; | ||
296 | volatile Serial_m2s_buffer_t serial_m2s_buffer = {}; | ||
297 | uint8_t volatile status0 = 0; | ||
298 | |||
299 | enum serial_transaction_id { | ||
300 | GET_SLAVE_MATRIX = 0, | ||
301 | # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
302 | PUT_RGBLIGHT, | ||
303 | # endif | ||
304 | }; | ||
305 | |||
306 | SSTD_t transactions[] = { | ||
307 | [GET_SLAVE_MATRIX] = | ||
308 | { | ||
309 | (uint8_t *)&status0, | ||
310 | sizeof(serial_m2s_buffer), | ||
311 | (uint8_t *)&serial_m2s_buffer, | ||
312 | sizeof(serial_s2m_buffer), | ||
313 | (uint8_t *)&serial_s2m_buffer, | ||
314 | }, | ||
315 | # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
316 | [PUT_RGBLIGHT] = | ||
317 | { | ||
318 | (uint8_t *)&status_rgblight, sizeof(serial_rgblight), (uint8_t *)&serial_rgblight, 0, NULL // no slave to master transfer | ||
319 | }, | ||
320 | # endif | ||
321 | }; | ||
322 | |||
323 | void transport_master_init(void) { soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); } | ||
324 | |||
325 | void transport_slave_init(void) { soft_serial_target_init(transactions, TID_LIMIT(transactions)); } | ||
326 | 94 | ||
327 | # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | 95 | bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) { |
328 | 96 | split_transaction_desc_t *trans = &split_transaction_table[id]; | |
329 | // rgblight synchronization information communication. | 97 | if (initiator2target_length > 0) { |
330 | 98 | size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length; | |
331 | void transport_rgblight_master(void) { | 99 | memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len); |
332 | if (rgblight_get_change_flags()) { | ||
333 | rgblight_get_syncinfo((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync); | ||
334 | if (soft_serial_transaction(PUT_RGBLIGHT) == TRANSACTION_END) { | ||
335 | rgblight_clear_change_flags(); | ||
336 | } | ||
337 | } | ||
338 | } | ||
339 | |||
340 | void transport_rgblight_slave(void) { | ||
341 | if (status_rgblight == TRANSACTION_ACCEPTED) { | ||
342 | rgblight_update_sync((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync, false); | ||
343 | status_rgblight = TRANSACTION_END; | ||
344 | } | 100 | } |
345 | } | ||
346 | 101 | ||
347 | # else | 102 | if (soft_serial_transaction(id) != TRANSACTION_END) { |
348 | # define transport_rgblight_master() | ||
349 | # define transport_rgblight_slave() | ||
350 | # endif | ||
351 | |||
352 | bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | ||
353 | # ifndef SERIAL_USE_MULTI_TRANSACTION | ||
354 | if (soft_serial_transaction() != TRANSACTION_END) { | ||
355 | return false; | ||
356 | } | ||
357 | # else | ||
358 | transport_rgblight_master(); | ||
359 | if (soft_serial_transaction(GET_SLAVE_MATRIX) != TRANSACTION_END) { | ||
360 | return false; | 103 | return false; |
361 | } | 104 | } |
362 | # endif | ||
363 | 105 | ||
364 | // TODO: if MATRIX_COLS > 8 change to unpack() | 106 | if (target2initiator_length > 0) { |
365 | for (int i = 0; i < ROWS_PER_HAND; ++i) { | 107 | size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length; |
366 | slave_matrix[i] = serial_s2m_buffer.smatrix[i]; | 108 | memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len); |
367 | # ifdef SPLIT_TRANSPORT_MIRROR | ||
368 | serial_m2s_buffer.mmatrix[i] = master_matrix[i]; | ||
369 | # endif | ||
370 | } | 109 | } |
371 | 110 | ||
372 | # ifdef BACKLIGHT_ENABLE | ||
373 | // Write backlight level for slave to read | ||
374 | serial_m2s_buffer.backlight_level = is_backlight_enabled() ? get_backlight_level() : 0; | ||
375 | # endif | ||
376 | |||
377 | # ifdef ENCODER_ENABLE | ||
378 | encoder_update_raw((uint8_t *)serial_s2m_buffer.encoder_state); | ||
379 | # endif | ||
380 | |||
381 | # ifdef WPM_ENABLE | ||
382 | // Write wpm to slave | ||
383 | serial_m2s_buffer.current_wpm = get_current_wpm(); | ||
384 | # endif | ||
385 | |||
386 | # ifdef SPLIT_MODS_ENABLE | ||
387 | serial_m2s_buffer.real_mods = get_mods(); | ||
388 | serial_m2s_buffer.weak_mods = get_weak_mods(); | ||
389 | # ifndef NO_ACTION_ONESHOT | ||
390 | serial_m2s_buffer.oneshot_mods = get_oneshot_mods(); | ||
391 | # endif | ||
392 | # endif | ||
393 | |||
394 | # if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
395 | serial_m2s_buffer.led_matrix = led_matrix_eeconfig; | ||
396 | serial_m2s_buffer.led_suspend_state = led_matrix_get_suspend_state(); | ||
397 | # endif | ||
398 | # if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
399 | serial_m2s_buffer.rgb_matrix = rgb_matrix_config; | ||
400 | serial_m2s_buffer.rgb_suspend_state = rgb_matrix_get_suspend_state(); | ||
401 | # endif | ||
402 | |||
403 | # ifndef DISABLE_SYNC_TIMER | ||
404 | serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; | ||
405 | # endif | ||
406 | return true; | 111 | return true; |
407 | } | 112 | } |
408 | 113 | ||
409 | void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { | 114 | #endif // USE_I2C |
410 | transport_rgblight_slave(); | ||
411 | # ifndef DISABLE_SYNC_TIMER | ||
412 | sync_timer_update(serial_m2s_buffer.sync_timer); | ||
413 | # endif | ||
414 | |||
415 | // TODO: if MATRIX_COLS > 8 change to pack() | ||
416 | for (int i = 0; i < ROWS_PER_HAND; ++i) { | ||
417 | serial_s2m_buffer.smatrix[i] = slave_matrix[i]; | ||
418 | # ifdef SPLIT_TRANSPORT_MIRROR | ||
419 | master_matrix[i] = serial_m2s_buffer.mmatrix[i]; | ||
420 | # endif | ||
421 | } | ||
422 | # ifdef BACKLIGHT_ENABLE | ||
423 | backlight_set(serial_m2s_buffer.backlight_level); | ||
424 | # endif | ||
425 | |||
426 | # ifdef ENCODER_ENABLE | ||
427 | encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state); | ||
428 | # endif | ||
429 | 115 | ||
430 | # ifdef WPM_ENABLE | 116 | bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { return transactions_master(master_matrix, slave_matrix); } |
431 | set_current_wpm(serial_m2s_buffer.current_wpm); | ||
432 | # endif | ||
433 | |||
434 | # ifdef SPLIT_MODS_ENABLE | ||
435 | set_mods(serial_m2s_buffer.real_mods); | ||
436 | set_weak_mods(serial_m2s_buffer.weak_mods); | ||
437 | # ifndef NO_ACTION_ONESHOT | ||
438 | set_oneshot_mods(serial_m2s_buffer.oneshot_mods); | ||
439 | # endif | ||
440 | # endif | ||
441 | |||
442 | # if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
443 | led_matrix_eeconfig = serial_m2s_buffer.led_matrix; | ||
444 | led_matrix_set_suspend_state(serial_m2s_buffer.led_suspend_state); | ||
445 | # endif | ||
446 | # if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
447 | rgb_matrix_config = serial_m2s_buffer.rgb_matrix; | ||
448 | rgb_matrix_set_suspend_state(serial_m2s_buffer.rgb_suspend_state); | ||
449 | # endif | ||
450 | } | ||
451 | 117 | ||
452 | #endif | 118 | void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { transactions_slave(master_matrix, slave_matrix); } \ No newline at end of file |
diff --git a/quantum/split_common/transport.h b/quantum/split_common/transport.h index a9f66301b..2e07f6b25 100644 --- a/quantum/split_common/transport.h +++ b/quantum/split_common/transport.h | |||
@@ -1,10 +1,175 @@ | |||
1 | /* Copyright 2021 QMK | ||
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 | |||
1 | #pragma once | 17 | #pragma once |
2 | 18 | ||
19 | #include "stdint.h" | ||
20 | #include "stdbool.h" | ||
21 | |||
22 | #include "progmem.h" | ||
23 | #include "action_layer.h" | ||
3 | #include "matrix.h" | 24 | #include "matrix.h" |
4 | 25 | ||
26 | #ifndef RPC_M2S_BUFFER_SIZE | ||
27 | # define RPC_M2S_BUFFER_SIZE 32 | ||
28 | #endif // RPC_M2S_BUFFER_SIZE | ||
29 | |||
30 | #ifndef RPC_S2M_BUFFER_SIZE | ||
31 | # define RPC_S2M_BUFFER_SIZE 32 | ||
32 | #endif // RPC_S2M_BUFFER_SIZE | ||
33 | |||
5 | void transport_master_init(void); | 34 | void transport_master_init(void); |
6 | void transport_slave_init(void); | 35 | void transport_slave_init(void); |
7 | 36 | ||
8 | // returns false if valid data not received from slave | 37 | // returns false if valid data not received from slave |
9 | bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); | 38 | bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); |
10 | void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); | 39 | void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); |
40 | |||
41 | bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length); | ||
42 | |||
43 | #ifdef ENCODER_ENABLE | ||
44 | # include "encoder.h" | ||
45 | # define NUMBER_OF_ENCODERS (sizeof((pin_t[])ENCODERS_PAD_A) / sizeof(pin_t)) | ||
46 | #endif // ENCODER_ENABLE | ||
47 | |||
48 | #ifdef BACKLIGHT_ENABLE | ||
49 | # include "backlight.h" | ||
50 | #endif // BACKLIGHT_ENABLE | ||
51 | |||
52 | #ifdef RGBLIGHT_ENABLE | ||
53 | # include "rgblight.h" | ||
54 | #endif // RGBLIGHT_ENABLE | ||
55 | |||
56 | typedef struct _split_slave_matrix_sync_t { | ||
57 | uint8_t checksum; | ||
58 | matrix_row_t matrix[(MATRIX_ROWS) / 2]; | ||
59 | } split_slave_matrix_sync_t; | ||
60 | |||
61 | #ifdef SPLIT_TRANSPORT_MIRROR | ||
62 | typedef struct _split_master_matrix_sync_t { | ||
63 | matrix_row_t matrix[(MATRIX_ROWS) / 2]; | ||
64 | } split_master_matrix_sync_t; | ||
65 | #endif // SPLIT_TRANSPORT_MIRROR | ||
66 | |||
67 | #ifdef ENCODER_ENABLE | ||
68 | typedef struct _split_slave_encoder_sync_t { | ||
69 | uint8_t checksum; | ||
70 | uint8_t state[NUMBER_OF_ENCODERS]; | ||
71 | } split_slave_encoder_sync_t; | ||
72 | #endif // ENCODER_ENABLE | ||
73 | |||
74 | #if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) | ||
75 | typedef struct _split_layers_sync_t { | ||
76 | layer_state_t layer_state; | ||
77 | layer_state_t default_layer_state; | ||
78 | } split_layers_sync_t; | ||
79 | #endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) | ||
80 | |||
81 | #if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
82 | # include "led_matrix.h" | ||
83 | |||
84 | typedef struct _led_matrix_sync_t { | ||
85 | led_eeconfig_t led_matrix; | ||
86 | bool led_suspend_state; | ||
87 | } led_matrix_sync_t; | ||
88 | #endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
89 | |||
90 | #if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
91 | # include "rgb_matrix.h" | ||
92 | |||
93 | typedef struct _rgb_matrix_sync_t { | ||
94 | rgb_config_t rgb_matrix; | ||
95 | bool rgb_suspend_state; | ||
96 | } rgb_matrix_sync_t; | ||
97 | #endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
98 | |||
99 | #ifdef SPLIT_MODS_ENABLE | ||
100 | typedef struct _split_mods_sync_t { | ||
101 | uint8_t real_mods; | ||
102 | uint8_t weak_mods; | ||
103 | # ifndef NO_ACTION_ONESHOT | ||
104 | uint8_t oneshot_mods; | ||
105 | # endif // NO_ACTION_ONESHOT | ||
106 | } split_mods_sync_t; | ||
107 | #endif // SPLIT_MODS_ENABLE | ||
108 | |||
109 | #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
110 | typedef struct _rpc_sync_info_t { | ||
111 | int8_t transaction_id; | ||
112 | uint8_t m2s_length; | ||
113 | uint8_t s2m_length; | ||
114 | } rpc_sync_info_t; | ||
115 | #endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
116 | |||
117 | typedef struct _split_shared_memory_t { | ||
118 | #ifdef USE_I2C | ||
119 | int8_t transaction_id; | ||
120 | #endif // USE_I2C | ||
121 | |||
122 | split_slave_matrix_sync_t smatrix; | ||
123 | |||
124 | #ifdef SPLIT_TRANSPORT_MIRROR | ||
125 | split_master_matrix_sync_t mmatrix; | ||
126 | #endif // SPLIT_TRANSPORT_MIRROR | ||
127 | |||
128 | #ifdef ENCODER_ENABLE | ||
129 | split_slave_encoder_sync_t encoders; | ||
130 | #endif // ENCODER_ENABLE | ||
131 | |||
132 | #ifndef DISABLE_SYNC_TIMER | ||
133 | uint32_t sync_timer; | ||
134 | #endif // DISABLE_SYNC_TIMER | ||
135 | |||
136 | #if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) | ||
137 | split_layers_sync_t layers; | ||
138 | #endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) | ||
139 | |||
140 | #ifdef SPLIT_LED_STATE_ENABLE | ||
141 | uint8_t led_state; | ||
142 | #endif // SPLIT_LED_STATE_ENABLE | ||
143 | |||
144 | #ifdef SPLIT_MODS_ENABLE | ||
145 | split_mods_sync_t mods; | ||
146 | #endif // SPLIT_MODS_ENABLE | ||
147 | |||
148 | #ifdef BACKLIGHT_ENABLE | ||
149 | uint8_t backlight_level; | ||
150 | #endif // BACKLIGHT_ENABLE | ||
151 | |||
152 | #if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
153 | rgblight_syncinfo_t rgblight_sync; | ||
154 | #endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | ||
155 | |||
156 | #if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
157 | led_matrix_sync_t led_matrix_sync; | ||
158 | #endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) | ||
159 | |||
160 | #if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
161 | rgb_matrix_sync_t rgb_matrix_sync; | ||
162 | #endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) | ||
163 | |||
164 | #if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) | ||
165 | uint8_t current_wpm; | ||
166 | #endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) | ||
167 | |||
168 | #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
169 | rpc_sync_info_t rpc_info; | ||
170 | uint8_t rpc_m2s_buffer[RPC_M2S_BUFFER_SIZE]; | ||
171 | uint8_t rpc_s2m_buffer[RPC_S2M_BUFFER_SIZE]; | ||
172 | #endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) | ||
173 | } split_shared_memory_t; | ||
174 | |||
175 | extern split_shared_memory_t *const split_shmem; \ No newline at end of file | ||