diff options
| author | フィルターペーパー <76888457+filterpaper@users.noreply.github.com> | 2025-09-07 20:34:05 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-07 13:34:05 +0100 |
| commit | 4bd5c033c3135576010baded53c44720d8030d32 (patch) | |
| tree | 561a167570c79d39f62174d651d844a1c9cf2abc /quantum/debounce | |
| parent | 5830b1b5e3323b7a491824bef2b5bdf223e5d50e (diff) | |
Refactor debounce algorithm with static allocation (#25515)
* Refactor debounce counters with direct indexing
* Refactor code to use array indexing for debounce_counters
* Use global MATRIX_ROW_SHIFTER macro
* Refactor debounce algorithm with static allocation
* Converted arrays to static allocation
* Standardised use of MATRIX_ROWS_PER_HAND for array sizing
* Added Doxygen comments for primary debounce functions
* Removed debounce_free()
* Rewrite sym_defer_pr
* Modernise code using sym_defer_pk as template
* Format consistency with other current algorithms
* Use shorter SPDX-License-Identifier
* Remove ChibiOS core memory manager guard
* Keep type definition within DEBOUNCE guard
* Add change log
* Minor optimisation refactor
* Pre-calculate row_offset in per-key matrix loops
* Add inline compiler hints
* Improve readability with blank lines
* Limit elapsed time to the maximum debounce value
* Apply suggestions from code review
Declare counters with "DEBOUNCE_ELAPSED"
Co-authored-by: Joel Challis <git@zvecr.com>
* Update change log to new breaking change date
---------
Co-authored-by: Joel Challis <git@zvecr.com>
Diffstat (limited to 'quantum/debounce')
| -rw-r--r-- | quantum/debounce/asym_eager_defer_pk.c | 165 | ||||
| -rw-r--r-- | quantum/debounce/none.c | 2 | ||||
| -rw-r--r-- | quantum/debounce/sym_defer_g.c | 33 | ||||
| -rw-r--r-- | quantum/debounce/sym_defer_pk.c | 149 | ||||
| -rw-r--r-- | quantum/debounce/sym_defer_pr.c | 154 | ||||
| -rw-r--r-- | quantum/debounce/sym_eager_pk.c | 125 | ||||
| -rw-r--r-- | quantum/debounce/sym_eager_pr.c | 136 | ||||
| -rw-r--r-- | quantum/debounce/tests/debounce_test_common.cpp | 2 |
8 files changed, 371 insertions, 395 deletions
diff --git a/quantum/debounce/asym_eager_defer_pk.c b/quantum/debounce/asym_eager_defer_pk.c index b6fcdc3d4e..a385301c90 100644 --- a/quantum/debounce/asym_eager_defer_pk.c +++ b/quantum/debounce/asym_eager_defer_pk.c | |||
| @@ -1,37 +1,15 @@ | |||
| 1 | /* | 1 | // Copyright 2017 Alex Ong <the.onga@gmail.com> |
| 2 | * Copyright 2017 Alex Ong <the.onga@gmail.com> | 2 | // Copyright 2020 Andrei Purdea <andrei@purdea.ro> |
| 3 | * Copyright 2020 Andrei Purdea <andrei@purdea.ro> | 3 | // Copyright 2021 Simon Arlott |
| 4 | * Copyright 2021 Simon Arlott | 4 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 5 | * | 5 | // |
| 6 | * This program is free software: you can redistribute it and/or modify | 6 | // Asymetric per-key algorithm. After pressing a key, it immediately changes state, |
| 7 | * it under the terms of the GNU General Public License as published by | 7 | // with no further inputs accepted until DEBOUNCE milliseconds have occurred. After |
| 8 | * the Free Software Foundation, either version 2 of the License, or | 8 | // releasing a key, that state is pushed after no changes occur for DEBOUNCE milliseconds. |
| 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 | Asymetric per-key algorithm. After pressing a key, it immediately changes state, | ||
| 22 | with no further inputs accepted until DEBOUNCE milliseconds have occurred. After | ||
| 23 | releasing a key, that state is pushed after no changes occur for DEBOUNCE milliseconds. | ||
| 24 | */ | ||
| 25 | 9 | ||
| 26 | #include "debounce.h" | 10 | #include "debounce.h" |
| 27 | #include "timer.h" | 11 | #include "timer.h" |
| 28 | #include <stdlib.h> | 12 | #include "util.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 | 13 | ||
| 36 | #ifndef DEBOUNCE | 14 | #ifndef DEBOUNCE |
| 37 | # define DEBOUNCE 5 | 15 | # define DEBOUNCE 5 |
| @@ -43,44 +21,29 @@ releasing a key, that state is pushed after no changes occur for DEBOUNCE millis | |||
| 43 | # define DEBOUNCE 127 | 21 | # define DEBOUNCE 127 |
| 44 | #endif | 22 | #endif |
| 45 | 23 | ||
| 46 | #define ROW_SHIFTER ((matrix_row_t)1) | 24 | #define DEBOUNCE_ELAPSED 0 |
| 47 | 25 | ||
| 26 | #if DEBOUNCE > 0 | ||
| 48 | typedef struct { | 27 | typedef struct { |
| 49 | bool pressed : 1; | 28 | bool pressed : 1; |
| 50 | uint8_t time : 7; | 29 | uint8_t time : 7; |
| 51 | } debounce_counter_t; | 30 | } debounce_counter_t; |
| 52 | 31 | ||
| 53 | #if DEBOUNCE > 0 | 32 | // Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards |
| 54 | static debounce_counter_t *debounce_counters; | 33 | static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED}; |
| 55 | static fast_timer_t last_time; | 34 | static bool counters_need_update; |
| 56 | static bool counters_need_update; | 35 | static bool matrix_need_update; |
| 57 | static bool matrix_need_update; | 36 | static bool cooked_changed; |
| 58 | static bool cooked_changed; | ||
| 59 | |||
| 60 | # define DEBOUNCE_ELAPSED 0 | ||
| 61 | |||
| 62 | 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); | ||
| 63 | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); | ||
| 64 | |||
| 65 | // we use num_rows rather than MATRIX_ROWS to support split keyboards | ||
| 66 | void debounce_init(uint8_t num_rows) { | ||
| 67 | debounce_counters = malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t)); | ||
| 68 | int i = 0; | ||
| 69 | for (uint8_t r = 0; r < num_rows; r++) { | ||
| 70 | for (uint8_t c = 0; c < MATRIX_COLS; c++) { | ||
| 71 | debounce_counters[i++].time = DEBOUNCE_ELAPSED; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | 37 | ||
| 76 | void debounce_free(void) { | 38 | static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time); |
| 77 | free(debounce_counters); | 39 | static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]); |
| 78 | debounce_counters = NULL; | 40 | |
| 79 | } | 41 | void debounce_init(uint8_t num_rows) {} |
| 80 | 42 | ||
| 81 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 43 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { |
| 82 | bool updated_last = false; | 44 | static fast_timer_t last_time; |
| 83 | cooked_changed = false; | 45 | bool updated_last = false; |
| 46 | cooked_changed = false; | ||
| 84 | 47 | ||
| 85 | if (counters_need_update) { | 48 | if (counters_need_update) { |
| 86 | fast_timer_t now = timer_read_fast(); | 49 | fast_timer_t now = timer_read_fast(); |
| @@ -88,12 +51,10 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool | |||
| 88 | 51 | ||
| 89 | last_time = now; | 52 | last_time = now; |
| 90 | updated_last = true; | 53 | updated_last = true; |
| 91 | if (elapsed_time > UINT8_MAX) { | ||
| 92 | elapsed_time = UINT8_MAX; | ||
| 93 | } | ||
| 94 | 54 | ||
| 95 | if (elapsed_time > 0) { | 55 | if (elapsed_time > 0) { |
| 96 | update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time); | 56 | // Update debounce counters with elapsed timer clamped to 127 (maximum debounce) |
| 57 | update_debounce_counters_and_transfer_if_expired(raw, cooked, MIN(elapsed_time, 127)); | ||
| 97 | } | 58 | } |
| 98 | } | 59 | } |
| 99 | 60 | ||
| @@ -102,74 +63,96 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool | |||
| 102 | last_time = timer_read_fast(); | 63 | last_time = timer_read_fast(); |
| 103 | } | 64 | } |
| 104 | 65 | ||
| 105 | transfer_matrix_values(raw, cooked, num_rows); | 66 | transfer_matrix_values(raw, cooked); |
| 106 | } | 67 | } |
| 107 | 68 | ||
| 108 | return cooked_changed; | 69 | return cooked_changed; |
| 109 | } | 70 | } |
| 110 | 71 | ||
| 111 | 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) { | 72 | /** |
| 112 | debounce_counter_t *debounce_pointer = debounce_counters; | 73 | * @brief Processes per-key debounce counters and updates the debounced matrix state. |
| 113 | 74 | * | |
| 75 | * This function iterates through each key in the matrix and updates its debounce counter | ||
| 76 | * based on the elapsed time. If the debounce period has expired, the debounced state is | ||
| 77 | * updated accordingly for key-down (eager) and key-up (defer) events. | ||
| 78 | * | ||
| 79 | * @param raw The current raw key state matrix. | ||
| 80 | * @param cooked The debounced key state matrix to be updated. | ||
| 81 | * @param elapsed_time The time elapsed since the last debounce update, in milliseconds. | ||
| 82 | */ | ||
| 83 | static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time) { | ||
| 114 | counters_need_update = false; | 84 | counters_need_update = false; |
| 115 | matrix_need_update = false; | 85 | matrix_need_update = false; |
| 116 | 86 | ||
| 117 | for (uint8_t row = 0; row < num_rows; row++) { | 87 | for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { |
| 88 | uint16_t row_offset = row * MATRIX_COLS; | ||
| 89 | |||
| 118 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { | 90 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { |
| 119 | matrix_row_t col_mask = (ROW_SHIFTER << col); | 91 | uint16_t index = row_offset + col; |
| 120 | 92 | ||
| 121 | if (debounce_pointer->time != DEBOUNCE_ELAPSED) { | 93 | if (debounce_counters[index].time != DEBOUNCE_ELAPSED) { |
| 122 | if (debounce_pointer->time <= elapsed_time) { | 94 | if (debounce_counters[index].time <= elapsed_time) { |
| 123 | debounce_pointer->time = DEBOUNCE_ELAPSED; | 95 | debounce_counters[index].time = DEBOUNCE_ELAPSED; |
| 124 | 96 | ||
| 125 | if (debounce_pointer->pressed) { | 97 | if (debounce_counters[index].pressed) { |
| 126 | // key-down: eager | 98 | // key-down: eager |
| 127 | matrix_need_update = true; | 99 | matrix_need_update = true; |
| 128 | } else { | 100 | } else { |
| 129 | // key-up: defer | 101 | // key-up: defer |
| 102 | matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col); | ||
| 130 | matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask); | 103 | matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask); |
| 131 | cooked_changed |= cooked_next ^ cooked[row]; | 104 | cooked_changed |= cooked_next ^ cooked[row]; |
| 132 | cooked[row] = cooked_next; | 105 | cooked[row] = cooked_next; |
| 133 | } | 106 | } |
| 134 | } else { | 107 | } else { |
| 135 | debounce_pointer->time -= elapsed_time; | 108 | debounce_counters[index].time -= elapsed_time; |
| 136 | counters_need_update = true; | 109 | counters_need_update = true; |
| 137 | } | 110 | } |
| 138 | } | 111 | } |
| 139 | debounce_pointer++; | ||
| 140 | } | 112 | } |
| 141 | } | 113 | } |
| 142 | } | 114 | } |
| 143 | 115 | ||
| 144 | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { | 116 | /** |
| 145 | debounce_counter_t *debounce_pointer = debounce_counters; | 117 | * @brief Applies debounced changes to the matrix state based on per-key counters. |
| 146 | 118 | * | |
| 119 | * This function compares the raw and cooked key state matrices to detect changes. | ||
| 120 | * For each key, it updates the debounce counter and the debounced state according | ||
| 121 | * to the debounce algorithm. Key-down events are handled eagerly, while key-up | ||
| 122 | * events are deferred until the debounce period has elapsed. | ||
| 123 | * | ||
| 124 | * @param raw The current raw key state matrix. | ||
| 125 | * @param cooked The debounced key state matrix to be updated. | ||
| 126 | */ | ||
| 127 | static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]) { | ||
| 147 | matrix_need_update = false; | 128 | matrix_need_update = false; |
| 148 | 129 | ||
| 149 | for (uint8_t row = 0; row < num_rows; row++) { | 130 | for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { |
| 150 | matrix_row_t delta = raw[row] ^ cooked[row]; | 131 | uint16_t row_offset = row * MATRIX_COLS; |
| 132 | matrix_row_t delta = raw[row] ^ cooked[row]; | ||
| 133 | |||
| 151 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { | 134 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { |
| 152 | matrix_row_t col_mask = (ROW_SHIFTER << col); | 135 | uint16_t index = row_offset + col; |
| 136 | matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col); | ||
| 153 | 137 | ||
| 154 | if (delta & col_mask) { | 138 | if (delta & col_mask) { |
| 155 | if (debounce_pointer->time == DEBOUNCE_ELAPSED) { | 139 | if (debounce_counters[index].time == DEBOUNCE_ELAPSED) { |
| 156 | debounce_pointer->pressed = (raw[row] & col_mask); | 140 | debounce_counters[index].pressed = (raw[row] & col_mask); |
| 157 | debounce_pointer->time = DEBOUNCE; | 141 | debounce_counters[index].time = DEBOUNCE; |
| 158 | counters_need_update = true; | 142 | counters_need_update = true; |
| 159 | 143 | ||
| 160 | if (debounce_pointer->pressed) { | 144 | if (debounce_counters[index].pressed) { |
| 161 | // key-down: eager | 145 | // key-down: eager |
| 162 | cooked[row] ^= col_mask; | 146 | cooked[row] ^= col_mask; |
| 163 | cooked_changed = true; | 147 | cooked_changed = true; |
| 164 | } | 148 | } |
| 165 | } | 149 | } |
| 166 | } else if (debounce_pointer->time != DEBOUNCE_ELAPSED) { | 150 | } else if (debounce_counters[index].time != DEBOUNCE_ELAPSED) { |
| 167 | if (!debounce_pointer->pressed) { | 151 | if (!debounce_counters[index].pressed) { |
| 168 | // key-up: defer | 152 | // key-up: defer |
| 169 | debounce_pointer->time = DEBOUNCE_ELAPSED; | 153 | debounce_counters[index].time = DEBOUNCE_ELAPSED; |
| 170 | } | 154 | } |
| 171 | } | 155 | } |
| 172 | debounce_pointer++; | ||
| 173 | } | 156 | } |
| 174 | } | 157 | } |
| 175 | } | 158 | } |
diff --git a/quantum/debounce/none.c b/quantum/debounce/none.c index 0a8ccfc4ee..0111dd6e31 100644 --- a/quantum/debounce/none.c +++ b/quantum/debounce/none.c | |||
| @@ -32,5 +32,3 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool | |||
| 32 | 32 | ||
| 33 | return cooked_changed; | 33 | return cooked_changed; |
| 34 | } | 34 | } |
| 35 | |||
| 36 | void debounce_free(void) {} | ||
diff --git a/quantum/debounce/sym_defer_g.c b/quantum/debounce/sym_defer_g.c index d96758fab3..81f351c126 100644 --- a/quantum/debounce/sym_defer_g.c +++ b/quantum/debounce/sym_defer_g.c | |||
| @@ -1,22 +1,10 @@ | |||
| 1 | /* | 1 | // Copyright 2017 Alex Ong<the.onga@gmail.com> |
| 2 | Copyright 2017 Alex Ong<the.onga@gmail.com> | 2 | // Copyright 2021 Simon Arlott |
| 3 | Copyright 2021 Simon Arlott | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | This program is free software: you can redistribute it and/or modify | 4 | // |
| 5 | it under the terms of the GNU General Public License as published by | 5 | // Basic global debounce algorithm. Used in 99% of keyboards at time of implementation |
| 6 | the Free Software Foundation, either version 2 of the License, or | 6 | // When no state changes have occured for DEBOUNCE milliseconds, we push the state. |
| 7 | (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 | You should have received a copy of the GNU General Public License | ||
| 13 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 14 | */ | ||
| 15 | |||
| 16 | /* | ||
| 17 | Basic global debounce algorithm. Used in 99% of keyboards at time of implementation | ||
| 18 | When no state changes have occured for DEBOUNCE milliseconds, we push the state. | ||
| 19 | */ | ||
| 20 | #include "debounce.h" | 8 | #include "debounce.h" |
| 21 | #include "timer.h" | 9 | #include "timer.h" |
| 22 | #include <string.h> | 10 | #include <string.h> |
| @@ -31,13 +19,13 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state. | |||
| 31 | #endif | 19 | #endif |
| 32 | 20 | ||
| 33 | #if DEBOUNCE > 0 | 21 | #if DEBOUNCE > 0 |
| 34 | static bool debouncing = false; | ||
| 35 | static fast_timer_t debouncing_time; | ||
| 36 | 22 | ||
| 37 | void debounce_init(uint8_t num_rows) {} | 23 | void debounce_init(uint8_t num_rows) {} |
| 38 | 24 | ||
| 39 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 25 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { |
| 40 | bool cooked_changed = false; | 26 | static fast_timer_t debouncing_time; |
| 27 | static bool debouncing = false; | ||
| 28 | bool cooked_changed = false; | ||
| 41 | 29 | ||
| 42 | if (changed) { | 30 | if (changed) { |
| 43 | debouncing = true; | 31 | debouncing = true; |
| @@ -54,7 +42,6 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool | |||
| 54 | return cooked_changed; | 42 | return cooked_changed; |
| 55 | } | 43 | } |
| 56 | 44 | ||
| 57 | void debounce_free(void) {} | ||
| 58 | #else // no debouncing. | 45 | #else // no debouncing. |
| 59 | # include "none.c" | 46 | # include "none.c" |
| 60 | #endif | 47 | #endif |
diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c index 156535a373..063094efe5 100644 --- a/quantum/debounce/sym_defer_pk.c +++ b/quantum/debounce/sym_defer_pk.c | |||
| @@ -1,33 +1,14 @@ | |||
| 1 | /* | 1 | // Copyright 2017 Alex Ong<the.onga@gmail.com> |
| 2 | Copyright 2017 Alex Ong<the.onga@gmail.com> | 2 | // Copyright 2020 Andrei Purdea<andrei@purdea.ro> |
| 3 | Copyright 2020 Andrei Purdea<andrei@purdea.ro> | 3 | // Copyright 2021 Simon Arlott |
| 4 | Copyright 2021 Simon Arlott | 4 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 5 | This program is free software: you can redistribute it and/or modify | 5 | // |
| 6 | it under the terms of the GNU General Public License as published by | 6 | // Basic symmetric per-key algorithm. Uses an 8-bit counter per key. |
| 7 | the Free Software Foundation, either version 2 of the License, or | 7 | // When no state changes have occured for DEBOUNCE milliseconds, we push the state. |
| 8 | (at your option) any later version. | ||
| 9 | This program is distributed in the hope that it will be useful, | ||
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | GNU General Public License for more details. | ||
| 13 | 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 | /* | ||
| 18 | Basic symmetric per-key algorithm. Uses an 8-bit counter per key. | ||
| 19 | When no state changes have occured for DEBOUNCE milliseconds, we push the state. | ||
| 20 | */ | ||
| 21 | 8 | ||
| 22 | #include "debounce.h" | 9 | #include "debounce.h" |
| 23 | #include "timer.h" | 10 | #include "timer.h" |
| 24 | #include <stdlib.h> | 11 | #include "util.h" |
| 25 | |||
| 26 | #ifdef PROTOCOL_CHIBIOS | ||
| 27 | # if CH_CFG_USE_MEMCORE == FALSE | ||
| 28 | # 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. | ||
| 29 | # endif | ||
| 30 | #endif | ||
| 31 | 12 | ||
| 32 | #ifndef DEBOUNCE | 13 | #ifndef DEBOUNCE |
| 33 | # define DEBOUNCE 5 | 14 | # define DEBOUNCE 5 |
| @@ -39,40 +20,24 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state. | |||
| 39 | # define DEBOUNCE UINT8_MAX | 20 | # define DEBOUNCE UINT8_MAX |
| 40 | #endif | 21 | #endif |
| 41 | 22 | ||
| 42 | #define ROW_SHIFTER ((matrix_row_t)1) | 23 | #define DEBOUNCE_ELAPSED 0 |
| 43 | 24 | ||
| 25 | #if DEBOUNCE > 0 | ||
| 44 | typedef uint8_t debounce_counter_t; | 26 | typedef uint8_t debounce_counter_t; |
| 27 | // Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards | ||
| 28 | static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED}; | ||
| 29 | static bool counters_need_update; | ||
| 30 | static bool cooked_changed; | ||
| 45 | 31 | ||
| 46 | #if DEBOUNCE > 0 | 32 | static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time); |
| 47 | static debounce_counter_t *debounce_counters; | 33 | static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]); |
| 48 | static fast_timer_t last_time; | ||
| 49 | static bool counters_need_update; | ||
| 50 | static bool cooked_changed; | ||
| 51 | |||
| 52 | # define DEBOUNCE_ELAPSED 0 | ||
| 53 | |||
| 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); | ||
| 55 | static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); | ||
| 56 | |||
| 57 | // we use num_rows rather than MATRIX_ROWS to support split keyboards | ||
| 58 | void debounce_init(uint8_t num_rows) { | ||
| 59 | debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t)); | ||
| 60 | int i = 0; | ||
| 61 | for (uint8_t r = 0; r < num_rows; r++) { | ||
| 62 | for (uint8_t c = 0; c < MATRIX_COLS; c++) { | ||
| 63 | debounce_counters[i++] = DEBOUNCE_ELAPSED; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | 34 | ||
| 68 | void debounce_free(void) { | 35 | void debounce_init(uint8_t num_rows) {} |
| 69 | free(debounce_counters); | ||
| 70 | debounce_counters = NULL; | ||
| 71 | } | ||
| 72 | 36 | ||
| 73 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 37 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { |
| 74 | bool updated_last = false; | 38 | static fast_timer_t last_time; |
| 75 | cooked_changed = false; | 39 | bool updated_last = false; |
| 40 | cooked_changed = false; | ||
| 76 | 41 | ||
| 77 | if (counters_need_update) { | 42 | if (counters_need_update) { |
| 78 | fast_timer_t now = timer_read_fast(); | 43 | fast_timer_t now = timer_read_fast(); |
| @@ -80,12 +45,10 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool | |||
| 80 | 45 | ||
| 81 | last_time = now; | 46 | last_time = now; |
| 82 | updated_last = true; | 47 | updated_last = true; |
| 83 | if (elapsed_time > UINT8_MAX) { | ||
| 84 | elapsed_time = UINT8_MAX; | ||
| 85 | } | ||
| 86 | 48 | ||
| 87 | if (elapsed_time > 0) { | 49 | if (elapsed_time > 0) { |
| 88 | update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time); | 50 | // Update debounce counters with elapsed timer clamped to UINT8_MAX |
| 51 | update_debounce_counters_and_transfer_if_expired(raw, cooked, MIN(elapsed_time, UINT8_MAX)); | ||
| 89 | } | 52 | } |
| 90 | } | 53 | } |
| 91 | 54 | ||
| @@ -94,47 +57,73 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool | |||
| 94 | last_time = timer_read_fast(); | 57 | last_time = timer_read_fast(); |
| 95 | } | 58 | } |
| 96 | 59 | ||
| 97 | start_debounce_counters(raw, cooked, num_rows); | 60 | start_debounce_counters(raw, cooked); |
| 98 | } | 61 | } |
| 99 | 62 | ||
| 100 | return cooked_changed; | 63 | return cooked_changed; |
| 101 | } | 64 | } |
| 102 | 65 | ||
| 103 | 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) { | 66 | /** |
| 104 | counters_need_update = false; | 67 | * @brief Updates debounce counters and transfers debounced key states if the debounce period has expired. |
| 105 | debounce_counter_t *debounce_pointer = debounce_counters; | 68 | * |
| 106 | for (uint8_t row = 0; row < num_rows; row++) { | 69 | * Iterates through each key in the matrix and checks its debounce counter. If the debounce period has expired |
| 70 | * for a key, the debounced state is updated to match the raw state. Otherwise, the debounce counter is decremented | ||
| 71 | * by the elapsed time and marked for further updates. | ||
| 72 | * | ||
| 73 | * @param raw The current raw key state matrix. | ||
| 74 | * @param cooked The debounced key state matrix to be updated. | ||
| 75 | * @param elapsed_time The time elapsed since the last debounce update, in milliseconds. | ||
| 76 | */ | ||
| 77 | static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time) { | ||
| 78 | counters_need_update = false; | ||
| 79 | for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { | ||
| 80 | uint16_t row_offset = row * MATRIX_COLS; | ||
| 81 | |||
| 107 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { | 82 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { |
| 108 | if (*debounce_pointer != DEBOUNCE_ELAPSED) { | 83 | uint16_t index = row_offset + col; |
| 109 | if (*debounce_pointer <= elapsed_time) { | 84 | |
| 110 | *debounce_pointer = DEBOUNCE_ELAPSED; | 85 | if (debounce_counters[index] != DEBOUNCE_ELAPSED) { |
| 111 | matrix_row_t cooked_next = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col)); | 86 | if (debounce_counters[index] <= elapsed_time) { |
| 87 | debounce_counters[index] = DEBOUNCE_ELAPSED; | ||
| 88 | matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col); | ||
| 89 | matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask); | ||
| 112 | cooked_changed |= cooked[row] ^ cooked_next; | 90 | cooked_changed |= cooked[row] ^ cooked_next; |
| 113 | cooked[row] = cooked_next; | 91 | cooked[row] = cooked_next; |
| 114 | } else { | 92 | } else { |
| 115 | *debounce_pointer -= elapsed_time; | 93 | debounce_counters[index] -= elapsed_time; |
| 116 | counters_need_update = true; | 94 | counters_need_update = true; |
| 117 | } | 95 | } |
| 118 | } | 96 | } |
| 119 | debounce_pointer++; | ||
| 120 | } | 97 | } |
| 121 | } | 98 | } |
| 122 | } | 99 | } |
| 123 | 100 | ||
| 124 | static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { | 101 | /** |
| 125 | debounce_counter_t *debounce_pointer = debounce_counters; | 102 | * @brief Initializes debounce counters for keys with changed states. |
| 126 | for (uint8_t row = 0; row < num_rows; row++) { | 103 | * |
| 127 | matrix_row_t delta = raw[row] ^ cooked[row]; | 104 | * For each key in the matrix, this function checks if the raw state differs from the debounced state. |
| 105 | * If a change is detected and the debounce counter has elapsed, the counter is set to the debounce period | ||
| 106 | * and marked for update. Otherwise, the counter is cleared. | ||
| 107 | * | ||
| 108 | * @param raw The current raw key state matrix. | ||
| 109 | * @param cooked The debounced key state matrix. | ||
| 110 | */ | ||
| 111 | static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]) { | ||
| 112 | for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { | ||
| 113 | uint16_t row_offset = row * MATRIX_COLS; | ||
| 114 | matrix_row_t delta = raw[row] ^ cooked[row]; | ||
| 115 | |||
| 128 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { | 116 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { |
| 129 | if (delta & (ROW_SHIFTER << col)) { | 117 | uint16_t index = row_offset + col; |
| 130 | if (*debounce_pointer == DEBOUNCE_ELAPSED) { | 118 | |
| 131 | *debounce_pointer = DEBOUNCE; | 119 | if (delta & (MATRIX_ROW_SHIFTER << col)) { |
| 132 | counters_need_update = true; | 120 | if (debounce_counters[index] == DEBOUNCE_ELAPSED) { |
| 121 | debounce_counters[index] = DEBOUNCE; | ||
| 122 | counters_need_update = true; | ||
| 133 | } | 123 | } |
| 134 | } else { | 124 | } else { |
| 135 | *debounce_pointer = DEBOUNCE_ELAPSED; | 125 | debounce_counters[index] = DEBOUNCE_ELAPSED; |
| 136 | } | 126 | } |
| 137 | debounce_pointer++; | ||
| 138 | } | 127 | } |
| 139 | } | 128 | } |
| 140 | } | 129 | } |
diff --git a/quantum/debounce/sym_defer_pr.c b/quantum/debounce/sym_defer_pr.c index d6222af5b2..2382fae898 100644 --- a/quantum/debounce/sym_defer_pr.c +++ b/quantum/debounce/sym_defer_pr.c | |||
| @@ -1,77 +1,117 @@ | |||
| 1 | /* | 1 | // Copyright 2017 Alex Ong<the.onga@gmail.com> |
| 2 | Copyright 2021 Chad Austin <chad@chadaustin.me> | 2 | // Copyright 2020 Andrei Purdea<andrei@purdea.ro> |
| 3 | This program is free software: you can redistribute it and/or modify | 3 | // Copyright 2021 Simon Arlott |
| 4 | it under the terms of the GNU General Public License as published by | 4 | // Copyright @filterpaper |
| 5 | the Free Software Foundation, either version 2 of the License, or | 5 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 6 | (at your option) any later version. | 6 | // |
| 7 | This program is distributed in the hope that it will be useful, | 7 | // Basic symmetric per-row algorithm. Uses an 8-bit counter per row. |
| 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 8 | // When no state changes have occured for DEBOUNCE milliseconds, we push the state. |
| 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 10 | GNU General Public License for more details. | ||
| 11 | You should have received a copy of the GNU General Public License | ||
| 12 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 13 | */ | ||
| 14 | |||
| 15 | /* | ||
| 16 | Symmetric per-row debounce algorithm. Changes only apply when | ||
| 17 | DEBOUNCE milliseconds have elapsed since the last change. | ||
| 18 | */ | ||
| 19 | 9 | ||
| 20 | #include "debounce.h" | 10 | #include "debounce.h" |
| 21 | #include "timer.h" | 11 | #include "timer.h" |
| 22 | #include <stdlib.h> | 12 | #include "util.h" |
| 23 | 13 | ||
| 24 | #ifndef DEBOUNCE | 14 | #ifndef DEBOUNCE |
| 25 | # define DEBOUNCE 5 | 15 | # define DEBOUNCE 5 |
| 26 | #endif | 16 | #endif |
| 27 | 17 | ||
| 28 | static uint16_t last_time; | 18 | // Maximum debounce: 255ms |
| 29 | // [row] milliseconds until key's state is considered debounced. | 19 | #if DEBOUNCE > UINT8_MAX |
| 30 | static uint8_t* countdowns; | 20 | # undef DEBOUNCE |
| 31 | // [row] | 21 | # define DEBOUNCE UINT8_MAX |
| 32 | static matrix_row_t* last_raw; | 22 | #endif |
| 33 | 23 | ||
| 34 | void debounce_init(uint8_t num_rows) { | 24 | #define DEBOUNCE_ELAPSED 0 |
| 35 | countdowns = (uint8_t*)calloc(num_rows, sizeof(uint8_t)); | ||
| 36 | last_raw = (matrix_row_t*)calloc(num_rows, sizeof(matrix_row_t)); | ||
| 37 | 25 | ||
| 38 | last_time = timer_read(); | 26 | #if DEBOUNCE > 0 |
| 39 | } | 27 | typedef uint8_t debounce_counter_t; |
| 28 | // Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards | ||
| 29 | static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND] = {DEBOUNCE_ELAPSED}; | ||
| 30 | static bool counters_need_update; | ||
| 31 | static bool cooked_changed; | ||
| 40 | 32 | ||
| 41 | void debounce_free(void) { | 33 | static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time); |
| 42 | free(countdowns); | 34 | static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]); |
| 43 | countdowns = NULL; | 35 | |
| 44 | free(last_raw); | 36 | void debounce_init(uint8_t num_rows) {} |
| 45 | last_raw = NULL; | ||
| 46 | } | ||
| 47 | 37 | ||
| 48 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 38 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { |
| 49 | uint16_t now = timer_read(); | 39 | static fast_timer_t last_time; |
| 50 | uint16_t elapsed16 = TIMER_DIFF_16(now, last_time); | 40 | bool updated_last = false; |
| 51 | last_time = now; | 41 | cooked_changed = false; |
| 52 | uint8_t elapsed = (elapsed16 > 255) ? 255 : elapsed16; | 42 | |
| 53 | bool cooked_changed = false; | 43 | if (counters_need_update) { |
| 54 | 44 | fast_timer_t now = timer_read_fast(); | |
| 55 | uint8_t* countdown = countdowns; | 45 | fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); |
| 56 | 46 | ||
| 57 | for (uint8_t row = 0; row < num_rows; ++row, ++countdown) { | 47 | last_time = now; |
| 58 | matrix_row_t raw_row = raw[row]; | 48 | updated_last = true; |
| 59 | 49 | ||
| 60 | if (raw_row != last_raw[row]) { | 50 | if (elapsed_time > 0) { |
| 61 | *countdown = DEBOUNCE; | 51 | // Update debounce counters with elapsed timer clamped to UINT8_MAX |
| 62 | last_raw[row] = raw_row; | 52 | update_debounce_counters_and_transfer_if_expired(raw, cooked, MIN(elapsed_time, UINT8_MAX)); |
| 63 | } else if (*countdown > elapsed) { | ||
| 64 | *countdown -= elapsed; | ||
| 65 | } else if (*countdown) { | ||
| 66 | cooked_changed |= cooked[row] ^ raw_row; | ||
| 67 | cooked[row] = raw_row; | ||
| 68 | *countdown = 0; | ||
| 69 | } | 53 | } |
| 70 | } | 54 | } |
| 71 | 55 | ||
| 56 | if (changed) { | ||
| 57 | if (!updated_last) { | ||
| 58 | last_time = timer_read_fast(); | ||
| 59 | } | ||
| 60 | |||
| 61 | start_debounce_counters(raw, cooked); | ||
| 62 | } | ||
| 63 | |||
| 72 | return cooked_changed; | 64 | return cooked_changed; |
| 73 | } | 65 | } |
| 74 | 66 | ||
| 75 | bool debounce_active(void) { | 67 | /** |
| 76 | return true; | 68 | * @brief Updates debounce counters and transfers debounced row states if the debounce period has expired. |
| 69 | * | ||
| 70 | * Iterates through each row in the matrix and checks its debounce counter. If the debounce period has expired | ||
| 71 | * for a row, the debounced state is updated to match the raw state. Otherwise, the debounce counter is decremented | ||
| 72 | * by the elapsed time and marked for further updates. | ||
| 73 | * | ||
| 74 | * @param raw The current raw key state matrix. | ||
| 75 | * @param cooked The debounced key state matrix to be updated. | ||
| 76 | * @param elapsed_time The time elapsed since the last debounce update, in milliseconds. | ||
| 77 | */ | ||
| 78 | static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time) { | ||
| 79 | counters_need_update = false; | ||
| 80 | for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { | ||
| 81 | if (debounce_counters[row] != DEBOUNCE_ELAPSED) { | ||
| 82 | if (debounce_counters[row] <= elapsed_time) { | ||
| 83 | debounce_counters[row] = DEBOUNCE_ELAPSED; | ||
| 84 | cooked_changed |= cooked[row] ^ raw[row]; | ||
| 85 | cooked[row] = raw[row]; | ||
| 86 | } else { | ||
| 87 | debounce_counters[row] -= elapsed_time; | ||
| 88 | counters_need_update = true; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 77 | } | 92 | } |
| 93 | |||
| 94 | /** | ||
| 95 | * @brief Initializes debounce counters for rows with changed states. | ||
| 96 | * | ||
| 97 | * For each row in the matrix, this function checks if the raw state differs from the debounced state. | ||
| 98 | * If a change is detected and the debounce counter has elapsed, the counter is set to the debounce period | ||
| 99 | * and marked for update. Otherwise, the counter is cleared. | ||
| 100 | * | ||
| 101 | * @param raw The current raw key state matrix. | ||
| 102 | * @param cooked The debounced key state matrix. | ||
| 103 | */ | ||
| 104 | static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]) { | ||
| 105 | for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { | ||
| 106 | if (raw[row] != cooked[row]) { | ||
| 107 | debounce_counters[row] = DEBOUNCE; | ||
| 108 | counters_need_update = true; | ||
| 109 | } else { | ||
| 110 | debounce_counters[row] = DEBOUNCE_ELAPSED; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | #else | ||
| 116 | # include "none.c" | ||
| 117 | #endif | ||
diff --git a/quantum/debounce/sym_eager_pk.c b/quantum/debounce/sym_eager_pk.c index b359e79287..c3a7afde24 100644 --- a/quantum/debounce/sym_eager_pk.c +++ b/quantum/debounce/sym_eager_pk.c | |||
| @@ -21,13 +21,7 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred. | |||
| 21 | 21 | ||
| 22 | #include "debounce.h" | 22 | #include "debounce.h" |
| 23 | #include "timer.h" | 23 | #include "timer.h" |
| 24 | #include <stdlib.h> | 24 | #include "util.h" |
| 25 | |||
| 26 | #ifdef PROTOCOL_CHIBIOS | ||
| 27 | # if CH_CFG_USE_MEMCORE == FALSE | ||
| 28 | # 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. | ||
| 29 | # endif | ||
| 30 | #endif | ||
| 31 | 25 | ||
| 32 | #ifndef DEBOUNCE | 26 | #ifndef DEBOUNCE |
| 33 | # define DEBOUNCE 5 | 27 | # define DEBOUNCE 5 |
| @@ -39,41 +33,25 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred. | |||
| 39 | # define DEBOUNCE UINT8_MAX | 33 | # define DEBOUNCE UINT8_MAX |
| 40 | #endif | 34 | #endif |
| 41 | 35 | ||
| 42 | #define ROW_SHIFTER ((matrix_row_t)1) | 36 | #define DEBOUNCE_ELAPSED 0 |
| 43 | 37 | ||
| 38 | #if DEBOUNCE > 0 | ||
| 44 | typedef uint8_t debounce_counter_t; | 39 | typedef uint8_t debounce_counter_t; |
| 40 | // Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards | ||
| 41 | static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED}; | ||
| 42 | static bool counters_need_update; | ||
| 43 | static bool matrix_need_update; | ||
| 44 | static bool cooked_changed; | ||
| 45 | 45 | ||
| 46 | #if DEBOUNCE > 0 | 46 | static inline void update_debounce_counters(uint8_t elapsed_time); |
| 47 | static debounce_counter_t *debounce_counters; | 47 | static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]); |
| 48 | static fast_timer_t last_time; | ||
| 49 | static bool counters_need_update; | ||
| 50 | static bool matrix_need_update; | ||
| 51 | static bool cooked_changed; | ||
| 52 | |||
| 53 | # define DEBOUNCE_ELAPSED 0 | ||
| 54 | |||
| 55 | static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); | ||
| 56 | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); | ||
| 57 | |||
| 58 | // we use num_rows rather than MATRIX_ROWS to support split keyboards | ||
| 59 | void debounce_init(uint8_t num_rows) { | ||
| 60 | debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t)); | ||
| 61 | int i = 0; | ||
| 62 | for (uint8_t r = 0; r < num_rows; r++) { | ||
| 63 | for (uint8_t c = 0; c < MATRIX_COLS; c++) { | ||
| 64 | debounce_counters[i++] = DEBOUNCE_ELAPSED; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | 48 | ||
| 69 | void debounce_free(void) { | 49 | void debounce_init(uint8_t num_rows) {} |
| 70 | free(debounce_counters); | ||
| 71 | debounce_counters = NULL; | ||
| 72 | } | ||
| 73 | 50 | ||
| 74 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 51 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { |
| 75 | bool updated_last = false; | 52 | static fast_timer_t last_time; |
| 76 | cooked_changed = false; | 53 | bool updated_last = false; |
| 54 | cooked_changed = false; | ||
| 77 | 55 | ||
| 78 | if (counters_need_update) { | 56 | if (counters_need_update) { |
| 79 | fast_timer_t now = timer_read_fast(); | 57 | fast_timer_t now = timer_read_fast(); |
| @@ -81,12 +59,10 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool | |||
| 81 | 59 | ||
| 82 | last_time = now; | 60 | last_time = now; |
| 83 | updated_last = true; | 61 | updated_last = true; |
| 84 | if (elapsed_time > UINT8_MAX) { | ||
| 85 | elapsed_time = UINT8_MAX; | ||
| 86 | } | ||
| 87 | 62 | ||
| 88 | if (elapsed_time > 0) { | 63 | if (elapsed_time > 0) { |
| 89 | update_debounce_counters(num_rows, elapsed_time); | 64 | // Update debounce counters with elapsed timer clamped to UINT8_MAX |
| 65 | update_debounce_counters(MIN(elapsed_time, UINT8_MAX)); | ||
| 90 | } | 66 | } |
| 91 | } | 67 | } |
| 92 | 68 | ||
| @@ -95,51 +71,74 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool | |||
| 95 | last_time = timer_read_fast(); | 71 | last_time = timer_read_fast(); |
| 96 | } | 72 | } |
| 97 | 73 | ||
| 98 | transfer_matrix_values(raw, cooked, num_rows); | 74 | transfer_matrix_values(raw, cooked); |
| 99 | } | 75 | } |
| 100 | 76 | ||
| 101 | return cooked_changed; | 77 | return cooked_changed; |
| 102 | } | 78 | } |
| 103 | 79 | ||
| 104 | // If the current time is > debounce counter, set the counter to enable input. | 80 | /** |
| 105 | static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) { | 81 | * @brief Updates per-key debounce counters and determines if matrix needs updating. |
| 106 | counters_need_update = false; | 82 | * |
| 107 | matrix_need_update = false; | 83 | * Iterates through each key in the matrix and checks its debounce counter. If the debounce |
| 108 | debounce_counter_t *debounce_pointer = debounce_counters; | 84 | * period has elapsed, the counter is reset and the matrix is marked for update. Otherwise, |
| 109 | for (uint8_t row = 0; row < num_rows; row++) { | 85 | * the counter is decremented by the elapsed time and marked for further updates if needed. |
| 86 | * | ||
| 87 | * @param elapsed_time The time elapsed since the last debounce update, in milliseconds. | ||
| 88 | */ | ||
| 89 | static inline void update_debounce_counters(uint8_t elapsed_time) { | ||
| 90 | counters_need_update = false; | ||
| 91 | matrix_need_update = false; | ||
| 92 | |||
| 93 | for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { | ||
| 94 | uint16_t row_offset = row * MATRIX_COLS; | ||
| 95 | |||
| 110 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { | 96 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { |
| 111 | if (*debounce_pointer != DEBOUNCE_ELAPSED) { | 97 | uint16_t index = row_offset + col; |
| 112 | if (*debounce_pointer <= elapsed_time) { | 98 | |
| 113 | *debounce_pointer = DEBOUNCE_ELAPSED; | 99 | if (debounce_counters[index] != DEBOUNCE_ELAPSED) { |
| 114 | matrix_need_update = true; | 100 | if (debounce_counters[index] <= elapsed_time) { |
| 101 | debounce_counters[index] = DEBOUNCE_ELAPSED; | ||
| 102 | matrix_need_update = true; | ||
| 115 | } else { | 103 | } else { |
| 116 | *debounce_pointer -= elapsed_time; | 104 | debounce_counters[index] -= elapsed_time; |
| 117 | counters_need_update = true; | 105 | counters_need_update = true; |
| 118 | } | 106 | } |
| 119 | } | 107 | } |
| 120 | debounce_pointer++; | ||
| 121 | } | 108 | } |
| 122 | } | 109 | } |
| 123 | } | 110 | } |
| 124 | 111 | ||
| 125 | // upload from raw_matrix to final matrix; | 112 | /** |
| 126 | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { | 113 | * @brief Transfers debounced key states from the raw matrix to the cooked matrix. |
| 127 | matrix_need_update = false; | 114 | * |
| 128 | debounce_counter_t *debounce_pointer = debounce_counters; | 115 | * For each key in the matrix, this function checks if its state has changed and if its |
| 129 | for (uint8_t row = 0; row < num_rows; row++) { | 116 | * debounce counter has elapsed. If so, the debounce counter is reset, the cooked matrix |
| 117 | * is updated to reflect the new state, and the matrix is marked for further updates. | ||
| 118 | * | ||
| 119 | * @param raw The current raw key state matrix. | ||
| 120 | * @param cooked The debounced key state matrix to be updated. | ||
| 121 | */ | ||
| 122 | static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]) { | ||
| 123 | matrix_need_update = false; | ||
| 124 | |||
| 125 | for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { | ||
| 126 | uint16_t row_offset = row * MATRIX_COLS; | ||
| 130 | matrix_row_t delta = raw[row] ^ cooked[row]; | 127 | matrix_row_t delta = raw[row] ^ cooked[row]; |
| 131 | matrix_row_t existing_row = cooked[row]; | 128 | matrix_row_t existing_row = cooked[row]; |
| 129 | |||
| 132 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { | 130 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { |
| 133 | matrix_row_t col_mask = (ROW_SHIFTER << col); | 131 | uint16_t index = row_offset + col; |
| 132 | |||
| 133 | matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col); | ||
| 134 | if (delta & col_mask) { | 134 | if (delta & col_mask) { |
| 135 | if (*debounce_pointer == DEBOUNCE_ELAPSED) { | 135 | if (debounce_counters[index] == DEBOUNCE_ELAPSED) { |
| 136 | *debounce_pointer = DEBOUNCE; | 136 | debounce_counters[index] = DEBOUNCE; |
| 137 | counters_need_update = true; | 137 | counters_need_update = true; |
| 138 | existing_row ^= col_mask; // flip the bit. | 138 | existing_row ^= col_mask; // flip the bit. |
| 139 | cooked_changed = true; | 139 | cooked_changed = true; |
| 140 | } | 140 | } |
| 141 | } | 141 | } |
| 142 | debounce_pointer++; | ||
| 143 | } | 142 | } |
| 144 | cooked[row] = existing_row; | 143 | cooked[row] = existing_row; |
| 145 | } | 144 | } |
diff --git a/quantum/debounce/sym_eager_pr.c b/quantum/debounce/sym_eager_pr.c index 6cd9308aff..5a1e3a1bda 100644 --- a/quantum/debounce/sym_eager_pr.c +++ b/quantum/debounce/sym_eager_pr.c | |||
| @@ -1,33 +1,14 @@ | |||
| 1 | /* | 1 | // Copyright 2017 Alex Ong<the.onga@gmail.com> |
| 2 | Copyright 2019 Alex Ong<the.onga@gmail.com> | 2 | // Copyright 2021 Simon Arlott |
| 3 | Copyright 2021 Simon Arlott | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | This program is free software: you can redistribute it and/or modify | 4 | // |
| 5 | it under the terms of the GNU General Public License as published by | 5 | // Basic per-row algorithm. Uses an 8-bit counter per key. |
| 6 | the Free Software Foundation, either version 2 of the License, or | 6 | // After pressing a key, it immediately changes state, and sets a counter. |
| 7 | (at your option) any later version. | 7 | // No further inputs are accepted until DEBOUNCE milliseconds have occurred. |
| 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 | You should have received a copy of the GNU General Public License | ||
| 13 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 14 | */ | ||
| 15 | |||
| 16 | /* | ||
| 17 | Basic per-row algorithm. Uses an 8-bit counter per row. | ||
| 18 | After pressing a key, it immediately changes state, and sets a counter. | ||
| 19 | No further inputs are accepted until DEBOUNCE milliseconds have occurred. | ||
| 20 | */ | ||
| 21 | 8 | ||
| 22 | #include "debounce.h" | 9 | #include "debounce.h" |
| 23 | #include "timer.h" | 10 | #include "timer.h" |
| 24 | #include <stdlib.h> | 11 | #include "util.h" |
| 25 | |||
| 26 | #ifdef PROTOCOL_CHIBIOS | ||
| 27 | # if CH_CFG_USE_MEMCORE == FALSE | ||
| 28 | # 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. | ||
| 29 | # endif | ||
| 30 | #endif | ||
| 31 | 12 | ||
| 32 | #ifndef DEBOUNCE | 13 | #ifndef DEBOUNCE |
| 33 | # define DEBOUNCE 5 | 14 | # define DEBOUNCE 5 |
| @@ -39,37 +20,25 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred. | |||
| 39 | # define DEBOUNCE UINT8_MAX | 20 | # define DEBOUNCE UINT8_MAX |
| 40 | #endif | 21 | #endif |
| 41 | 22 | ||
| 42 | typedef uint8_t debounce_counter_t; | 23 | #define DEBOUNCE_ELAPSED 0 |
| 43 | 24 | ||
| 44 | #if DEBOUNCE > 0 | 25 | #if DEBOUNCE > 0 |
| 45 | static bool matrix_need_update; | 26 | typedef uint8_t debounce_counter_t; |
| 46 | 27 | // Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards | |
| 47 | static debounce_counter_t *debounce_counters; | 28 | static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND] = {DEBOUNCE_ELAPSED}; |
| 48 | static fast_timer_t last_time; | 29 | static bool counters_need_update; |
| 49 | static bool counters_need_update; | 30 | static bool matrix_need_update; |
| 50 | static bool cooked_changed; | 31 | static bool cooked_changed; |
| 51 | |||
| 52 | # define DEBOUNCE_ELAPSED 0 | ||
| 53 | |||
| 54 | static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); | ||
| 55 | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); | ||
| 56 | 32 | ||
| 57 | // we use num_rows rather than MATRIX_ROWS to support split keyboards | 33 | static inline void update_debounce_counters(uint8_t elapsed_time); |
| 58 | void debounce_init(uint8_t num_rows) { | 34 | static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]); |
| 59 | debounce_counters = (debounce_counter_t *)malloc(num_rows * sizeof(debounce_counter_t)); | ||
| 60 | for (uint8_t r = 0; r < num_rows; r++) { | ||
| 61 | debounce_counters[r] = DEBOUNCE_ELAPSED; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | 35 | ||
| 65 | void debounce_free(void) { | 36 | void debounce_init(uint8_t num_rows) {} |
| 66 | free(debounce_counters); | ||
| 67 | debounce_counters = NULL; | ||
| 68 | } | ||
| 69 | 37 | ||
| 70 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 38 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { |
| 71 | bool updated_last = false; | 39 | static fast_timer_t last_time; |
| 72 | cooked_changed = false; | 40 | bool updated_last = false; |
| 41 | cooked_changed = false; | ||
| 73 | 42 | ||
| 74 | if (counters_need_update) { | 43 | if (counters_need_update) { |
| 75 | fast_timer_t now = timer_read_fast(); | 44 | fast_timer_t now = timer_read_fast(); |
| @@ -77,12 +46,10 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool | |||
| 77 | 46 | ||
| 78 | last_time = now; | 47 | last_time = now; |
| 79 | updated_last = true; | 48 | updated_last = true; |
| 80 | if (elapsed_time > UINT8_MAX) { | ||
| 81 | elapsed_time = UINT8_MAX; | ||
| 82 | } | ||
| 83 | 49 | ||
| 84 | if (elapsed_time > 0) { | 50 | if (elapsed_time > 0) { |
| 85 | update_debounce_counters(num_rows, elapsed_time); | 51 | // Update debounce counters with elapsed timer clamped to UINT8_MAX |
| 52 | update_debounce_counters(MIN(elapsed_time, UINT8_MAX)); | ||
| 86 | } | 53 | } |
| 87 | } | 54 | } |
| 88 | 55 | ||
| @@ -91,49 +58,64 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool | |||
| 91 | last_time = timer_read_fast(); | 58 | last_time = timer_read_fast(); |
| 92 | } | 59 | } |
| 93 | 60 | ||
| 94 | transfer_matrix_values(raw, cooked, num_rows); | 61 | transfer_matrix_values(raw, cooked); |
| 95 | } | 62 | } |
| 96 | 63 | ||
| 97 | return cooked_changed; | 64 | return cooked_changed; |
| 98 | } | 65 | } |
| 99 | 66 | ||
| 100 | // If the current time is > debounce counter, set the counter to enable input. | 67 | /** |
| 101 | static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) { | 68 | * @brief Updates per-row debounce counters and determines if matrix needs updating. |
| 102 | counters_need_update = false; | 69 | * |
| 103 | matrix_need_update = false; | 70 | * Iterates through each row in the matrix and checks its debounce counter. If the debounce |
| 104 | debounce_counter_t *debounce_pointer = debounce_counters; | 71 | * period has elapsed, the counter is reset and the matrix is marked for update. Otherwise, |
| 105 | for (uint8_t row = 0; row < num_rows; row++) { | 72 | * the counter is decremented by the elapsed time and marked for further updates if needed. |
| 106 | if (*debounce_pointer != DEBOUNCE_ELAPSED) { | 73 | * |
| 107 | if (*debounce_pointer <= elapsed_time) { | 74 | * @param elapsed_time The time elapsed since the last debounce update, in milliseconds. |
| 108 | *debounce_pointer = DEBOUNCE_ELAPSED; | 75 | */ |
| 109 | matrix_need_update = true; | 76 | static inline void update_debounce_counters(uint8_t elapsed_time) { |
| 77 | counters_need_update = false; | ||
| 78 | matrix_need_update = false; | ||
| 79 | |||
| 80 | for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { | ||
| 81 | if (debounce_counters[row] != DEBOUNCE_ELAPSED) { | ||
| 82 | if (debounce_counters[row] <= elapsed_time) { | ||
| 83 | debounce_counters[row] = DEBOUNCE_ELAPSED; | ||
| 84 | matrix_need_update = true; | ||
| 110 | } else { | 85 | } else { |
| 111 | *debounce_pointer -= elapsed_time; | 86 | debounce_counters[row] -= elapsed_time; |
| 112 | counters_need_update = true; | 87 | counters_need_update = true; |
| 113 | } | 88 | } |
| 114 | } | 89 | } |
| 115 | debounce_pointer++; | ||
| 116 | } | 90 | } |
| 117 | } | 91 | } |
| 118 | 92 | ||
| 119 | // upload from raw_matrix to final matrix; | 93 | /** |
| 120 | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { | 94 | * @brief Transfers debounced key states from the raw matrix to the cooked matrix. |
| 121 | matrix_need_update = false; | 95 | * |
| 122 | debounce_counter_t *debounce_pointer = debounce_counters; | 96 | * For each row in the matrix, this function checks if its state has changed and if its |
| 123 | for (uint8_t row = 0; row < num_rows; row++) { | 97 | * debounce counter has elapsed. If so, the debounce counter is reset, the cooked matrix |
| 98 | * is updated to reflect the new state, and the matrix is marked for further updates. | ||
| 99 | * | ||
| 100 | * @param raw The current raw key state matrix. | ||
| 101 | * @param cooked The debounced key state matrix | ||
| 102 | */ | ||
| 103 | static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]) { | ||
| 104 | matrix_need_update = false; | ||
| 105 | |||
| 106 | for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { | ||
| 124 | matrix_row_t existing_row = cooked[row]; | 107 | matrix_row_t existing_row = cooked[row]; |
| 125 | matrix_row_t raw_row = raw[row]; | 108 | matrix_row_t raw_row = raw[row]; |
| 126 | 109 | ||
| 127 | // determine new value basd on debounce pointer + raw value | 110 | // determine new value basd on debounce pointer + raw value |
| 128 | if (existing_row != raw_row) { | 111 | if (existing_row != raw_row) { |
| 129 | if (*debounce_pointer == DEBOUNCE_ELAPSED) { | 112 | if (debounce_counters[row] == DEBOUNCE_ELAPSED) { |
| 130 | *debounce_pointer = DEBOUNCE; | 113 | debounce_counters[row] = DEBOUNCE; |
| 131 | cooked_changed |= cooked[row] ^ raw_row; | 114 | cooked_changed |= cooked[row] ^ raw_row; |
| 132 | cooked[row] = raw_row; | 115 | cooked[row] = raw_row; |
| 133 | counters_need_update = true; | 116 | counters_need_update = true; |
| 134 | } | 117 | } |
| 135 | } | 118 | } |
| 136 | debounce_pointer++; | ||
| 137 | } | 119 | } |
| 138 | } | 120 | } |
| 139 | 121 | ||
diff --git a/quantum/debounce/tests/debounce_test_common.cpp b/quantum/debounce/tests/debounce_test_common.cpp index fd4b6f01a6..3782f51411 100644 --- a/quantum/debounce/tests/debounce_test_common.cpp +++ b/quantum/debounce/tests/debounce_test_common.cpp | |||
| @@ -121,8 +121,6 @@ void DebounceTest::runEventsInternal() { | |||
| 121 | checkCookedMatrix(false, "debounce() modified cooked matrix"); | 121 | checkCookedMatrix(false, "debounce() modified cooked matrix"); |
| 122 | advance_time(1); | 122 | advance_time(1); |
| 123 | } | 123 | } |
| 124 | |||
| 125 | debounce_free(); | ||
| 126 | } | 124 | } |
| 127 | 125 | ||
| 128 | void DebounceTest::runDebounce(bool changed) { | 126 | void DebounceTest::runDebounce(bool changed) { |
