From f7ccbfcea88e127319a4665a6c8ca67883854030 Mon Sep 17 00:00:00 2001 From: Purdea Andrei Date: Sun, 6 Sep 2020 23:13:49 +0300 Subject: quantum/debounce: rename debouncing algorithms (#9564) * quantum/debounce: rename debouncing algorithms according to Issue 8763 This is the second attempt at implementation, with no ts_ and cy_ prefixes, since those will be implemented with macros. * Debouncing documentation: Refactor, add some generic info, and merge into a single document --- common_features.mk | 13 +++- docs/feature_debounce_type.md | 146 ++++++++++++++++++++++++++++++++++------ quantum/debounce/eager_pk.c | 118 -------------------------------- quantum/debounce/eager_pr.c | 110 ------------------------------ quantum/debounce/readme.md | 28 -------- quantum/debounce/sym_defer_g.c | 52 ++++++++++++++ quantum/debounce/sym_defer_pk.c | 111 ++++++++++++++++++++++++++++++ quantum/debounce/sym_eager_pk.c | 118 ++++++++++++++++++++++++++++++++ quantum/debounce/sym_eager_pr.c | 110 ++++++++++++++++++++++++++++++ quantum/debounce/sym_g.c | 52 -------------- quantum/debounce/sym_pk.c | 111 ------------------------------ 11 files changed, 530 insertions(+), 439 deletions(-) delete mode 100644 quantum/debounce/eager_pk.c delete mode 100644 quantum/debounce/eager_pr.c delete mode 100644 quantum/debounce/readme.md create mode 100644 quantum/debounce/sym_defer_g.c create mode 100644 quantum/debounce/sym_defer_pk.c create mode 100644 quantum/debounce/sym_eager_pk.c create mode 100644 quantum/debounce/sym_eager_pr.c delete mode 100644 quantum/debounce/sym_g.c delete mode 100644 quantum/debounce/sym_pk.c diff --git a/common_features.mk b/common_features.mk index 1f110d0819..ed6908f4b8 100644 --- a/common_features.mk +++ b/common_features.mk @@ -397,9 +397,20 @@ ifneq ($(strip $(CUSTOM_MATRIX)), yes) endif endif +# Support for translating old names to new names: +ifeq ($(strip $(DEBOUNCE_TYPE)),sym_g) + DEBOUNCE_TYPE:=sym_defer_g +else ifeq ($(strip $(DEBOUNCE_TYPE)),eager_pk) + DEBOUNCE_TYPE:=sym_eager_pk +else ifeq ($(strip $(DEBOUNCE_TYPE)),sym_pk) + DEBOUNCE_TYPE:=sym_defer_pk +else ifeq ($(strip $(DEBOUNCE_TYPE)),eager_pr) + DEBOUNCE_TYPE:=sym_eager_pr +endif + DEBOUNCE_DIR:= $(QUANTUM_DIR)/debounce # Debounce Modules. Set DEBOUNCE_TYPE=custom if including one manually. -DEBOUNCE_TYPE?= sym_g +DEBOUNCE_TYPE?= sym_defer_g ifneq ($(strip $(DEBOUNCE_TYPE)), custom) QUANTUM_SRC += $(DEBOUNCE_DIR)/$(strip $(DEBOUNCE_TYPE)).c endif diff --git a/docs/feature_debounce_type.md b/docs/feature_debounce_type.md index 65b4ea1e53..83ebafe60e 100644 --- a/docs/feature_debounce_type.md +++ b/docs/feature_debounce_type.md @@ -1,43 +1,151 @@ -# Debounce algorithm +# Contact bounce / contact chatter -QMK supports multiple debounce algorithms through its debounce API. +Mechanical switches often don't have a clean single transition between pressed and unpressed states. + +In an ideal world, when you press a switch, you would expect the digital pin to see something like this: +(X axis showing time +``` +voltage +---------------------- + ^ | + | | + | ------------------+ + ----> time +``` + +However in the real world you will actually see contact bounce, which will look like multiple 1->0 and 0->1 transitions, +until the value finally settles. +``` + +-+ +--+ +------------- + | | | | | + | | | | | ++-----------------+ +-+ +-+ +``` +The time it takes for the switch to settle might vary with switch type, age, and even pressing technique. + +If the device chooses not to mitigate contact bounce, then often actions that happen when the switch is pressed are repeated +multiple times. + +There are many ways to handle contact bounce ("Debouncing"). Some include employing additional hardware, for example an RC filter, +while there are various ways to do debouncing in software too, often called debounce algorithms. This page discusses software +debouncing methods available in QMK. + +While technically not considered contact bounce/contact chatter, some switch technologies are susceptible to noise, meaning, +while the key is not changing state, sometimes short random 0->1 or 1->0 transitions might be read by the digital circuit, for example: +``` + +-+ + | | + | | ++-----------------+ +-------------------- +``` + +Many debounce methods (but not all) will also make the device resistant to noise. If you are working with a technology that is +susceptible to noise, you must choose a debounce method that will also mitigate noise for you. + +## Types of debounce algorithms -The logic for which debounce method called is below. It checks various defines that you have set in rules.mk +1) Unit of time: Timestamp (milliseconds) vs Cycles (scans) + * Debounce algorithms often have a 'debounce time' parameter, that specifies the maximum settling time of the switch contacts. + This time might be measured in various units: + * Cycles-based debouncing waits n cycles (scans), decreasing count by one each matrix_scan + * Timestamp-based debouncing stores the millisecond timestamp a change occurred, and does substraction to figure out time elapsed. + * Timestamp-based debouncing is usually superior, especially in the case of noise-resistant devices because settling times of physical + switches is specified in units of time, and should not depend on the matrix scan-rate of the keyboard. + * Cycles-based debouncing is sometimes considered inferior, because the settling time that it is able to compensate for depends on the + performance of the matrix scanning code. If you use cycles-based debouncing, and you significantly improve the performance of your scanning + code, you might end up with less effective debouncing. A situation in which cycles-based debouncing might be preferable is when + noise is present, and the scanning algorithm is slow, or variable speed. Even if your debounce algorithm is fundamentally noise-resistant, + if the scanning is slow, and you are using a timestamp-based algorithm, you might end up making a debouncing decision based on only two + sampled values, which will limit the noise-resistance of the algorithm. + * Currently all built-in debounce algorithms support timestamp-based debouncing only. In the future we might + implement cycles-based debouncing, and it will be selectable via a ```config.h``` macro. + +2) Symmetric vs Asymmetric + * Symmetric - apply the same debouncing algorithm, to both key-up and key-down events. + * Recommended naming convention: ```sym_*``` + * Asymmetric - apply different debouncing algorithms to key-down and key-up events. E.g. Eager key-down, Defer key-up. + * Recommended naming convention: ```asym_*``` followed by details of the type of algorithm in use, in order, for key-down and then key-up + +3) Eager vs Defer + * Eager - any key change is reported immediately. All further inputs for DEBOUNCE ms are ignored. + * Eager algorithms are not noise-resistant. + * Recommended naming conventions: + * ```sym_eager_*``` + * ```asym_eager_*_*```: key-down is using eager algorithm + * ```asym_*_eager_*```: key-up is using eager algorithm + * Defer - wait for no changes for DEBOUNCE ms before reporting change. + * Defer algorithms are noise-resistant + * Recommended naming conventions: + * ```sym_defer_*``` + * ```asym_defer_*_*```: key-down is using eager algorithm + * ```asym_*_defer_*```: key-up is using eager algorithm + +4) Global vs Per-Key vs Per-Row + * Global - one timer for all keys. Any key change state affects global timer + * Recommended naming convention: ```*_g``` + * Per-key - one timer per key + * Recommended naming convention: ```*_pk``` + * Per-row - one timer per row + * Recommended naming convention: ```*_pr``` + * Per-key and per-row algorithms consume more resources (in terms of performance, + and ram usage), but fast typists might prefer them over global. + +## Debounce algorithms supported by QMK + +QMK supports multiple debounce algorithms through its debounce API. +The logic for which debounce method called is below. It checks various defines that you have set in ```rules.mk``` ``` DEBOUNCE_DIR:= $(QUANTUM_DIR)/debounce -DEBOUNCE_TYPE?= sym_g +DEBOUNCE_TYPE?= sym_defer_g ifneq ($(strip $(DEBOUNCE_TYPE)), custom) QUANTUM_SRC += $(DEBOUNCE_DIR)/$(strip $(DEBOUNCE_TYPE)).c endif ``` -# Debounce selection +### Debounce selection | DEBOUNCE_TYPE | Description | What else is needed | | ------------- | --------------------------------------------------- | ----------------------------- | -| Not defined | Use the default algorithm, currently sym_g | Nothing | +| Not defined | Use the default algorithm, currently sym_defer_g | Nothing | | custom | Use your own debounce code | ```SRC += debounce.c``` add your own debounce.c and implement necessary functions | -| anything_else | Use another algorithm from quantum/debounce/* | Nothing | +| Anything Else | Use another algorithm from quantum/debounce/* | Nothing | **Regarding split keyboards**: The debounce code is compatible with split keyboards. -# Use your own debouncing code -* Set ```DEBOUNCE_TYPE = custom```. -* Add ```SRC += debounce.c``` +### Selecting an included debouncing method +Keyboards may select one of the already implemented debounce methods, by adding to ```rules.mk``` the following line: +``` +DEBOUNCE_TYPE = +``` +Where name of algorithm is one of: +* ```sym_defer_g``` - debouncing per keyboard. On any state change, a global timer is set. When ```DEBOUNCE``` milliseconds of no changes has occurred, all input changes are pushed. + * This is the current default algorithm. This is the highest performance algorithm with lowest memory usage, and it's also noise-resistant. +* ```sym_eager_pr``` - debouncing per row. On any state change, response is immediate, followed by locking the row ```DEBOUNCE``` milliseconds of no further input for that row. +For use in keyboards where refreshing ```NUM_KEYS``` 8-bit counters is computationally expensive / low scan rate, and fingers usually only hit one row at a time. This could be +appropriate for the ErgoDox models; the matrix is rotated 90°, and hence its "rows" are really columns, and each finger only hits a single "row" at a time in normal use. +* ```sym_eager_pk``` - debouncing per key. On any state change, response is immediate, followed by ```DEBOUNCE``` milliseconds of no further input for that key +* ```sym_defer_pk``` - debouncing per key. On any state change, a per-key timer is set. When ```DEBOUNCE``` milliseconds of no changes have occurred on that key, the key status change is pushed. + +### A couple algorithms that could be implemented in the future: +* ```sym_defer_pr``` +* ```sym_eager_g``` +* ```asym_eager_defer_pk``` + +### Use your own debouncing code +You have the option to implement you own debouncing algorithm. To do this: +* Set ```DEBOUNCE_TYPE = custom``` in ```rules.mk```. +* Add ```SRC += debounce.c``` in ```rules.mk``` * Add your own ```debounce.c```. Look at current implementations in ```quantum/debounce``` for examples. * Debouncing occurs after every raw matrix scan. * Use num_rows rather than MATRIX_ROWS, so that split keyboards are supported correctly. +* If the algorithm might be applicable to other keyboards, please consider adding it to ```quantum/debounce``` -# Changing between included debouncing methods -You can either use your own code, by including your own debounce.c, or switch to another included one. -Included debounce methods are: -* eager_pr - debouncing per row. On any state change, response is immediate, followed by locking the row ```DEBOUNCE``` milliseconds of no further input for that row. -For use in keyboards where refreshing ```NUM_KEYS``` 8-bit counters is computationally expensive / low scan rate, and fingers usually only hit one row at a time. This could be -appropriate for the ErgoDox models; the matrix is rotated 90°, and hence its "rows" are really columns, and each finger only hits a single "row" at a time in normal use. -* eager_pk - debouncing per key. On any state change, response is immediate, followed by ```DEBOUNCE``` milliseconds of no further input for that key -* sym_g - debouncing per keyboard. On any state change, a global timer is set. When ```DEBOUNCE``` milliseconds of no changes has occured, all input changes are pushed. -* sym_pk - debouncing per key. On any state change, a per-key timer is set. When ```DEBOUNCE``` milliseconds of no changes have occured on that key, the key status change is pushed. +### Old names +The following old names for existing algorithms will continue to be supported, however it is recommended to use the new names instead. +* sym_g - old name for sym_defer_g +* eager_pk - old name for sym_eager_pk +* sym_pk - old name for sym_defer_pk +* eager_pr - old name for sym_eager_pr diff --git a/quantum/debounce/eager_pk.c b/quantum/debounce/eager_pk.c deleted file mode 100644 index 93a40ad441..0000000000 --- a/quantum/debounce/eager_pk.c +++ /dev/null @@ -1,118 +0,0 @@ -/* -Copyright 2017 Alex Ong -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -/* -Basic per-key algorithm. Uses an 8-bit counter per key. -After pressing a key, it immediately changes state, and sets a counter. -No further inputs are accepted until DEBOUNCE milliseconds have occurred. -*/ - -#include "matrix.h" -#include "timer.h" -#include "quantum.h" -#include - -#ifndef DEBOUNCE -# define DEBOUNCE 5 -#endif - -#define ROW_SHIFTER ((matrix_row_t)1) - -#define debounce_counter_t uint8_t - -static debounce_counter_t *debounce_counters; -static bool counters_need_update; -static bool matrix_need_update; - -#define DEBOUNCE_ELAPSED 251 -#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) - -static uint8_t wrapping_timer_read(void) { - static uint16_t time = 0; - static uint8_t last_result = 0; - uint16_t new_time = timer_read(); - uint16_t diff = new_time - time; - time = new_time; - last_result = (last_result + diff) % (MAX_DEBOUNCE + 1); - return last_result; -} - -void update_debounce_counters(uint8_t num_rows, uint8_t current_time); -void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); - -// we use num_rows rather than MATRIX_ROWS to support split keyboards -void debounce_init(uint8_t num_rows) { - debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t)); - int i = 0; - for (uint8_t r = 0; r < num_rows; r++) { - for (uint8_t c = 0; c < MATRIX_COLS; c++) { - debounce_counters[i++] = DEBOUNCE_ELAPSED; - } - } -} - -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { - uint8_t current_time = wrapping_timer_read(); - if (counters_need_update) { - update_debounce_counters(num_rows, current_time); - } - - if (changed || matrix_need_update) { - transfer_matrix_values(raw, cooked, num_rows, current_time); - } -} - -// If the current time is > debounce counter, set the counter to enable input. -void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { - counters_need_update = false; - debounce_counter_t *debounce_pointer = debounce_counters; - for (uint8_t row = 0; row < num_rows; row++) { - for (uint8_t col = 0; col < MATRIX_COLS; col++) { - if (*debounce_pointer != DEBOUNCE_ELAPSED) { - if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { - *debounce_pointer = DEBOUNCE_ELAPSED; - } else { - counters_need_update = true; - } - } - debounce_pointer++; - } - } -} - -// upload from raw_matrix to final matrix; -void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { - matrix_need_update = false; - debounce_counter_t *debounce_pointer = debounce_counters; - for (uint8_t row = 0; row < num_rows; row++) { - matrix_row_t delta = raw[row] ^ cooked[row]; - matrix_row_t existing_row = cooked[row]; - for (uint8_t col = 0; col < MATRIX_COLS; col++) { - matrix_row_t col_mask = (ROW_SHIFTER << col); - if (delta & col_mask) { - if (*debounce_pointer == DEBOUNCE_ELAPSED) { - *debounce_pointer = current_time; - counters_need_update = true; - existing_row ^= col_mask; // flip the bit. - } else { - matrix_need_update = true; - } - } - debounce_pointer++; - } - cooked[row] = existing_row; - } -} - -bool debounce_active(void) { return true; } diff --git a/quantum/debounce/eager_pr.c b/quantum/debounce/eager_pr.c deleted file mode 100644 index d12931fddb..0000000000 --- a/quantum/debounce/eager_pr.c +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright 2019 Alex Ong -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -/* -Basic per-row algorithm. Uses an 8-bit counter per row. -After pressing a key, it immediately changes state, and sets a counter. -No further inputs are accepted until DEBOUNCE milliseconds have occurred. -*/ - -#include "matrix.h" -#include "timer.h" -#include "quantum.h" -#include - -#ifndef DEBOUNCE -# define DEBOUNCE 5 -#endif - -#define debounce_counter_t uint8_t -static bool matrix_need_update; - -static debounce_counter_t *debounce_counters; -static bool counters_need_update; - -#define DEBOUNCE_ELAPSED 251 -#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) - -static uint8_t wrapping_timer_read(void) { - static uint16_t time = 0; - static uint8_t last_result = 0; - uint16_t new_time = timer_read(); - uint16_t diff = new_time - time; - time = new_time; - last_result = (last_result + diff) % (MAX_DEBOUNCE + 1); - return last_result; -} - -void update_debounce_counters(uint8_t num_rows, uint8_t current_time); -void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); - -// we use num_rows rather than MATRIX_ROWS to support split keyboards -void debounce_init(uint8_t num_rows) { - debounce_counters = (debounce_counter_t *)malloc(num_rows * sizeof(debounce_counter_t)); - for (uint8_t r = 0; r < num_rows; r++) { - debounce_counters[r] = DEBOUNCE_ELAPSED; - } -} - -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { - uint8_t current_time = wrapping_timer_read(); - bool needed_update = counters_need_update; - if (counters_need_update) { - update_debounce_counters(num_rows, current_time); - } - - if (changed || (needed_update && !counters_need_update) || matrix_need_update) { - transfer_matrix_values(raw, cooked, num_rows, current_time); - } -} - -// If the current time is > debounce counter, set the counter to enable input. -void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { - counters_need_update = false; - debounce_counter_t *debounce_pointer = debounce_counters; - for (uint8_t row = 0; row < num_rows; row++) { - if (*debounce_pointer != DEBOUNCE_ELAPSED) { - if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { - *debounce_pointer = DEBOUNCE_ELAPSED; - } else { - counters_need_update = true; - } - } - debounce_pointer++; - } -} - -// upload from raw_matrix to final matrix; -void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { - matrix_need_update = false; - debounce_counter_t *debounce_pointer = debounce_counters; - for (uint8_t row = 0; row < num_rows; row++) { - matrix_row_t existing_row = cooked[row]; - matrix_row_t raw_row = raw[row]; - - // determine new value basd on debounce pointer + raw value - if (existing_row != raw_row) { - if (*debounce_pointer == DEBOUNCE_ELAPSED) { - *debounce_pointer = current_time; - cooked[row] = raw_row; - counters_need_update = true; - } else { - matrix_need_update = true; - } - } - debounce_pointer++; - } -} - -bool debounce_active(void) { return true; } diff --git a/quantum/debounce/readme.md b/quantum/debounce/readme.md deleted file mode 100644 index f77f78c764..0000000000 --- a/quantum/debounce/readme.md +++ /dev/null @@ -1,28 +0,0 @@ -Debounce algorithms belong in this folder. -Here are a few ideas - -1) Global vs Per-Key vs Per-Row - * Global - one timer for all keys. Any key change state affects global timer - * Per key - one timer per key - * Per row - one timer per row - -2) Eager vs symmetric vs asymmetric - * Eager - any key change is reported immediately. All further inputs for DEBOUNCE ms are ignored. - * Symmetric - wait for no changes for DEBOUNCE ms before reporting change - * Asymmetric - wait for different times depending on key-down/key-up. E.g. Eager key-down, DEBOUNCE ms key up. - -3) Timestamp vs cycles - * old old old code waits n cycles, decreasing count by one each matrix_scan - * newer code stores the millisecond the change occurred, and does subraction to figure out time elapsed. - * Timestamps are superior, i don't think cycles will ever be used again once upgraded. - -The default algorithm is symmetric and global. -Here are a few that could be implemented: - -sym_g.c -sym_pk.c -sym_pr.c -sym_pr_cycles.c -eager_g.c -eager_pk.c -eager_pr.c //could be used in ergo-dox! diff --git a/quantum/debounce/sym_defer_g.c b/quantum/debounce/sym_defer_g.c new file mode 100644 index 0000000000..3ed9055d2a --- /dev/null +++ b/quantum/debounce/sym_defer_g.c @@ -0,0 +1,52 @@ +/* +Copyright 2017 Alex Ong +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/* +Basic global debounce algorithm. Used in 99% of keyboards at time of implementation +When no state changes have occured for DEBOUNCE milliseconds, we push the state. +*/ +#include "matrix.h" +#include "timer.h" +#include "quantum.h" +#ifndef DEBOUNCE +# define DEBOUNCE 5 +#endif + +void debounce_init(uint8_t num_rows) {} +static bool debouncing = false; + +#if DEBOUNCE > 0 +static uint16_t debouncing_time; +void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { + if (changed) { + debouncing = true; + debouncing_time = timer_read(); + } + + if (debouncing && timer_elapsed(debouncing_time) > DEBOUNCE) { + for (int i = 0; i < num_rows; i++) { + cooked[i] = raw[i]; + } + debouncing = false; + } +} +#else // no debouncing. +void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { + for (int i = 0; i < num_rows; i++) { + cooked[i] = raw[i]; + } +} +#endif + +bool debounce_active(void) { return debouncing; } diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c new file mode 100644 index 0000000000..f404cf9c44 --- /dev/null +++ b/quantum/debounce/sym_defer_pk.c @@ -0,0 +1,111 @@ +/* +Copyright 2017 Alex Ong +Copyright 2020 Andrei Purdea +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/* +Basic symmetric per-key algorithm. Uses an 8-bit counter per key. +When no state changes have occured for DEBOUNCE milliseconds, we push the state. +*/ + +#include "matrix.h" +#include "timer.h" +#include "quantum.h" +#include + +#ifndef DEBOUNCE +# define DEBOUNCE 5 +#endif + +#define ROW_SHIFTER ((matrix_row_t)1) + +#define debounce_counter_t uint8_t + +static debounce_counter_t *debounce_counters; +static bool counters_need_update; + +#define DEBOUNCE_ELAPSED 251 +#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) + +static uint8_t wrapping_timer_read(void) { + static uint16_t time = 0; + static uint8_t last_result = 0; + uint16_t new_time = timer_read(); + uint16_t diff = new_time - time; + time = new_time; + last_result = (last_result + diff) % (MAX_DEBOUNCE + 1); + return last_result; +} + +void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); +void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); + +// we use num_rows rather than MATRIX_ROWS to support split keyboards +void debounce_init(uint8_t num_rows) { + debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t)); + int i = 0; + for (uint8_t r = 0; r < num_rows; r++) { + for (uint8_t c = 0; c < MATRIX_COLS; c++) { + debounce_counters[i++] = DEBOUNCE_ELAPSED; + } + } +} + +void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { + uint8_t current_time = wrapping_timer_read(); + if (counters_need_update) { + update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, current_time); + } + + if (changed) { + start_debounce_counters(raw, cooked, num_rows, current_time); + } +} + +void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { + counters_need_update = false; + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + if (*debounce_pointer != DEBOUNCE_ELAPSED) { + if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { + *debounce_pointer = DEBOUNCE_ELAPSED; + cooked[row] = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col)); + } else { + counters_need_update = true; + } + } + debounce_pointer++; + } + } +} + +void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + matrix_row_t delta = raw[row] ^ cooked[row]; + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + if (delta & (ROW_SHIFTER << col)) { + if (*debounce_pointer == DEBOUNCE_ELAPSED) { + *debounce_pointer = current_time; + counters_need_update = true; + } + } else { + *debounce_pointer = DEBOUNCE_ELAPSED; + } + debounce_pointer++; + } + } +} + +bool debounce_active(void) { return true; } diff --git a/quantum/debounce/sym_eager_pk.c b/quantum/debounce/sym_eager_pk.c new file mode 100644 index 0000000000..93a40ad441 --- /dev/null +++ b/quantum/debounce/sym_eager_pk.c @@ -0,0 +1,118 @@ +/* +Copyright 2017 Alex Ong +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/* +Basic per-key algorithm. Uses an 8-bit counter per key. +After pressing a key, it immediately changes state, and sets a counter. +No further inputs are accepted until DEBOUNCE milliseconds have occurred. +*/ + +#include "matrix.h" +#include "timer.h" +#include "quantum.h" +#include + +#ifndef DEBOUNCE +# define DEBOUNCE 5 +#endif + +#define ROW_SHIFTER ((matrix_row_t)1) + +#define debounce_counter_t uint8_t + +static debounce_counter_t *debounce_counters; +static bool counters_need_update; +static bool matrix_need_update; + +#define DEBOUNCE_ELAPSED 251 +#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) + +static uint8_t wrapping_timer_read(void) { + static uint16_t time = 0; + static uint8_t last_result = 0; + uint16_t new_time = timer_read(); + uint16_t diff = new_time - time; + time = new_time; + last_result = (last_result + diff) % (MAX_DEBOUNCE + 1); + return last_result; +} + +void update_debounce_counters(uint8_t num_rows, uint8_t current_time); +void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); + +// we use num_rows rather than MATRIX_ROWS to support split keyboards +void debounce_init(uint8_t num_rows) { + debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t)); + int i = 0; + for (uint8_t r = 0; r < num_rows; r++) { + for (uint8_t c = 0; c < MATRIX_COLS; c++) { + debounce_counters[i++] = DEBOUNCE_ELAPSED; + } + } +} + +void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { + uint8_t current_time = wrapping_timer_read(); + if (counters_need_update) { + update_debounce_counters(num_rows, current_time); + } + + if (changed || matrix_need_update) { + transfer_matrix_values(raw, cooked, num_rows, current_time); + } +} + +// If the current time is > debounce counter, set the counter to enable input. +void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { + counters_need_update = false; + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + if (*debounce_pointer != DEBOUNCE_ELAPSED) { + if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { + *debounce_pointer = DEBOUNCE_ELAPSED; + } else { + counters_need_update = true; + } + } + debounce_pointer++; + } + } +} + +// upload from raw_matrix to final matrix; +void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { + matrix_need_update = false; + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + matrix_row_t delta = raw[row] ^ cooked[row]; + matrix_row_t existing_row = cooked[row]; + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + matrix_row_t col_mask = (ROW_SHIFTER << col); + if (delta & col_mask) { + if (*debounce_pointer == DEBOUNCE_ELAPSED) { + *debounce_pointer = current_time; + counters_need_update = true; + existing_row ^= col_mask; // flip the bit. + } else { + matrix_need_update = true; + } + } + debounce_pointer++; + } + cooked[row] = existing_row; + } +} + +bool debounce_active(void) { return true; } diff --git a/quantum/debounce/sym_eager_pr.c b/quantum/debounce/sym_eager_pr.c new file mode 100644 index 0000000000..d12931fddb --- /dev/null +++ b/quantum/debounce/sym_eager_pr.c @@ -0,0 +1,110 @@ +/* +Copyright 2019 Alex Ong +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/* +Basic per-row algorithm. Uses an 8-bit counter per row. +After pressing a key, it immediately changes state, and sets a counter. +No further inputs are accepted until DEBOUNCE milliseconds have occurred. +*/ + +#include "matrix.h" +#include "timer.h" +#include "quantum.h" +#include + +#ifndef DEBOUNCE +# define DEBOUNCE 5 +#endif + +#define debounce_counter_t uint8_t +static bool matrix_need_update; + +static debounce_counter_t *debounce_counters; +static bool counters_need_update; + +#define DEBOUNCE_ELAPSED 251 +#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) + +static uint8_t wrapping_timer_read(void) { + static uint16_t time = 0; + static uint8_t last_result = 0; + uint16_t new_time = timer_read(); + uint16_t diff = new_time - time; + time = new_time; + last_result = (last_result + diff) % (MAX_DEBOUNCE + 1); + return last_result; +} + +void update_debounce_counters(uint8_t num_rows, uint8_t current_time); +void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); + +// we use num_rows rather than MATRIX_ROWS to support split keyboards +void debounce_init(uint8_t num_rows) { + debounce_counters = (debounce_counter_t *)malloc(num_rows * sizeof(debounce_counter_t)); + for (uint8_t r = 0; r < num_rows; r++) { + debounce_counters[r] = DEBOUNCE_ELAPSED; + } +} + +void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { + uint8_t current_time = wrapping_timer_read(); + bool needed_update = counters_need_update; + if (counters_need_update) { + update_debounce_counters(num_rows, current_time); + } + + if (changed || (needed_update && !counters_need_update) || matrix_need_update) { + transfer_matrix_values(raw, cooked, num_rows, current_time); + } +} + +// If the current time is > debounce counter, set the counter to enable input. +void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { + counters_need_update = false; + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + if (*debounce_pointer != DEBOUNCE_ELAPSED) { + if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { + *debounce_pointer = DEBOUNCE_ELAPSED; + } else { + counters_need_update = true; + } + } + debounce_pointer++; + } +} + +// upload from raw_matrix to final matrix; +void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { + matrix_need_update = false; + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + matrix_row_t existing_row = cooked[row]; + matrix_row_t raw_row = raw[row]; + + // determine new value basd on debounce pointer + raw value + if (existing_row != raw_row) { + if (*debounce_pointer == DEBOUNCE_ELAPSED) { + *debounce_pointer = current_time; + cooked[row] = raw_row; + counters_need_update = true; + } else { + matrix_need_update = true; + } + } + debounce_pointer++; + } +} + +bool debounce_active(void) { return true; } diff --git a/quantum/debounce/sym_g.c b/quantum/debounce/sym_g.c deleted file mode 100644 index 3ed9055d2a..0000000000 --- a/quantum/debounce/sym_g.c +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2017 Alex Ong -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -/* -Basic global debounce algorithm. Used in 99% of keyboards at time of implementation -When no state changes have occured for DEBOUNCE milliseconds, we push the state. -*/ -#include "matrix.h" -#include "timer.h" -#include "quantum.h" -#ifndef DEBOUNCE -# define DEBOUNCE 5 -#endif - -void debounce_init(uint8_t num_rows) {} -static bool debouncing = false; - -#if DEBOUNCE > 0 -static uint16_t debouncing_time; -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { - if (changed) { - debouncing = true; - debouncing_time = timer_read(); - } - - if (debouncing && timer_elapsed(debouncing_time) > DEBOUNCE) { - for (int i = 0; i < num_rows; i++) { - cooked[i] = raw[i]; - } - debouncing = false; - } -} -#else // no debouncing. -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { - for (int i = 0; i < num_rows; i++) { - cooked[i] = raw[i]; - } -} -#endif - -bool debounce_active(void) { return debouncing; } diff --git a/quantum/debounce/sym_pk.c b/quantum/debounce/sym_pk.c deleted file mode 100644 index f404cf9c44..0000000000 --- a/quantum/debounce/sym_pk.c +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright 2017 Alex Ong -Copyright 2020 Andrei Purdea -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -/* -Basic symmetric per-key algorithm. Uses an 8-bit counter per key. -When no state changes have occured for DEBOUNCE milliseconds, we push the state. -*/ - -#include "matrix.h" -#include "timer.h" -#include "quantum.h" -#include - -#ifndef DEBOUNCE -# define DEBOUNCE 5 -#endif - -#define ROW_SHIFTER ((matrix_row_t)1) - -#define debounce_counter_t uint8_t - -static debounce_counter_t *debounce_counters; -static bool counters_need_update; - -#define DEBOUNCE_ELAPSED 251 -#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) - -static uint8_t wrapping_timer_read(void) { - static uint16_t time = 0; - static uint8_t last_result = 0; - uint16_t new_time = timer_read(); - uint16_t diff = new_time - time; - time = new_time; - last_result = (last_result + diff) % (MAX_DEBOUNCE + 1); - return last_result; -} - -void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); -void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); - -// we use num_rows rather than MATRIX_ROWS to support split keyboards -void debounce_init(uint8_t num_rows) { - debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t)); - int i = 0; - for (uint8_t r = 0; r < num_rows; r++) { - for (uint8_t c = 0; c < MATRIX_COLS; c++) { - debounce_counters[i++] = DEBOUNCE_ELAPSED; - } - } -} - -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { - uint8_t current_time = wrapping_timer_read(); - if (counters_need_update) { - update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, current_time); - } - - if (changed) { - start_debounce_counters(raw, cooked, num_rows, current_time); - } -} - -void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { - counters_need_update = false; - debounce_counter_t *debounce_pointer = debounce_counters; - for (uint8_t row = 0; row < num_rows; row++) { - for (uint8_t col = 0; col < MATRIX_COLS; col++) { - if (*debounce_pointer != DEBOUNCE_ELAPSED) { - if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { - *debounce_pointer = DEBOUNCE_ELAPSED; - cooked[row] = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col)); - } else { - counters_need_update = true; - } - } - debounce_pointer++; - } - } -} - -void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { - debounce_counter_t *debounce_pointer = debounce_counters; - for (uint8_t row = 0; row < num_rows; row++) { - matrix_row_t delta = raw[row] ^ cooked[row]; - for (uint8_t col = 0; col < MATRIX_COLS; col++) { - if (delta & (ROW_SHIFTER << col)) { - if (*debounce_pointer == DEBOUNCE_ELAPSED) { - *debounce_pointer = current_time; - counters_need_update = true; - } - } else { - *debounce_pointer = DEBOUNCE_ELAPSED; - } - debounce_pointer++; - } - } -} - -bool debounce_active(void) { return true; } -- cgit v1.2.3