diff options
-rw-r--r-- | docs/feature_debounce_algo.md | 53 | ||||
-rw-r--r-- | quantum/matrix.c | 79 | ||||
-rw-r--r-- | tmk_core/common.mk | 17 | ||||
-rw-r--r-- | tmk_core/common/debounce.c | 22 | ||||
-rw-r--r-- | tmk_core/common/debounce.h | 21 | ||||
-rw-r--r-- | tmk_core/common/debounce/debounce_eager_pk.c | 124 | ||||
-rw-r--r-- | tmk_core/common/debounce/debounce_sym_g.c | 59 | ||||
-rw-r--r-- | tmk_core/common/debounce/readme.md | 28 | ||||
-rw-r--r-- | tmk_core/common/keyboard.c | 22 |
9 files changed, 332 insertions, 93 deletions
diff --git a/docs/feature_debounce_algo.md b/docs/feature_debounce_algo.md new file mode 100644 index 0000000000..2c694cdfbd --- /dev/null +++ b/docs/feature_debounce_algo.md @@ -0,0 +1,53 @@ +# Debounce algorithm + +QMK supports multiple debounce algorithms through its debounce API. + +The underlying debounce algorithm is determined by which matrix.c file you are using. + +The logic for which debounce method called is below. It checks various defines that you have set in rules.mk + +``` +ifeq ($(strip $(SPLIT_KEYBOARD)), yes) + # Do nothing, debouncing is inside matrix.c inside split_common +else ifeq ($(strip $(DEBOUNCE_ALGO)), manual) + # Do nothing. do your debouncing in matrix.c +else ifeq ($(strip $(DEBOUNCE_ALGO)), sym_g) + TMK_COMMON_SRC += $(DEBOUNCE)/debounce_sym_g.c +else ifeq ($(strip $(DEBOUNCE_ALGO)), eager_pk) + TMK_COMMON_SRC += $(DEBOUNCE)/debounce_eager_pk.c +else ifeq ($(strip $(CUSTOM_MATRIX)), yes) + # Do nothing. Custom matrix code. +else # default algorithm + TMK_COMMON_SRC += $(DEBOUNCE)/debounce_sym_g.c +endif +``` + +# Debounce selection +The following is for keyboards where ```SPLIT_KEYBOARD``` is **not** defined as ```YES``` + +| DEBOUNCE_ALGO | CUSTOM_MATRIX | Description | What to do | +| ------------- | -------------| --------------------------------------------------- | ----------------------------- | +| Not defined | Not defined | You are using the included matrix.c and debounce.c | Nothing. Debounce_sym_g used. | +| manual | Not defined | You are using the included matrix.c but your own debounce.c | ```SRC += debounce.c``` add your own debounce.c and implement necessary functions | +| sym_g / eager_pk | Not defined | You are using the included matrix.c and debounce.c | Nothing. Chosen debounce method used. | +| Not defined | YES | You have your own matrix.c, and your own debounce | Write the fully debounced matrix into matrix.c's matrix | +| manual | YES | Same as above | same as above | +| sym_g/ eager_pk | YES | You are using your own matrix.c, but included debounce | Write the raw matrix values into matrix.c's matrix | + +**Note**: +If ```SPLIT_KEYBOARD = YES``` is defined, the algorithm inside split_common will be used. +A future pull request will fix this to use the debounce.c code. + +# Use your own debouncing code +* Set ```DEBOUNCE_ALGO = manual```. +* Add ```SRC += debounce.c``` +* Add your own ```debounce.c```. Look at included debounce.c's for sample implementations. +* Debouncing occurs after every raw matrix scan. + +# 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: +* debounce_eager_pk - debouncing per key. On any state change, response is immediate, followed by ```DEBOUNCE_DELAY``` millseconds of no further input for that key +* debounce_sym_g - debouncing per keyboard. On any state change, a global timer is set. When ```DEBOUNCE_DELAY``` milliseconds of no changes has occured, all input changes are pushed. + + diff --git a/quantum/matrix.c b/quantum/matrix.c index 9b5ce33d23..292171490c 100644 --- a/quantum/matrix.c +++ b/quantum/matrix.c @@ -25,17 +25,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "quantum.h" -/* Set 0 if debouncing isn't needed */ - -#ifndef DEBOUNCING_DELAY -# define DEBOUNCING_DELAY 5 -#endif - -#if (DEBOUNCING_DELAY > 0) - static uint16_t debouncing_time; - static bool debouncing = false; -#endif - #if (MATRIX_COLS <= 8) # define print_matrix_header() print("\nr/c 01234567\n") # define print_matrix_row(row) print_bin_reverse8(matrix_get_row(row)) @@ -65,8 +54,6 @@ static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; /* matrix state(1:on, 0:off) */ static matrix_row_t matrix[MATRIX_ROWS]; -static matrix_row_t matrix_debouncing[MATRIX_ROWS]; - #if (DIODE_DIRECTION == COL2ROW) static void init_cols(void); @@ -120,30 +107,6 @@ uint8_t matrix_cols(void) { return MATRIX_COLS; } -// void matrix_power_up(void) { -// #if (DIODE_DIRECTION == COL2ROW) -// for (int8_t r = MATRIX_ROWS - 1; r >= 0; --r) { -// /* DDRxn */ -// _SFR_IO8((row_pins[r] >> 4) + 1) |= _BV(row_pins[r] & 0xF); -// toggle_row(r); -// } -// for (int8_t c = MATRIX_COLS - 1; c >= 0; --c) { -// /* PORTxn */ -// _SFR_IO8((col_pins[c] >> 4) + 2) |= _BV(col_pins[c] & 0xF); -// } -// #elif (DIODE_DIRECTION == ROW2COL) -// for (int8_t c = MATRIX_COLS - 1; c >= 0; --c) { -// /* DDRxn */ -// _SFR_IO8((col_pins[c] >> 4) + 1) |= _BV(col_pins[c] & 0xF); -// toggle_col(c); -// } -// for (int8_t r = MATRIX_ROWS - 1; r >= 0; --r) { -// /* PORTxn */ -// _SFR_IO8((row_pins[r] >> 4) + 2) |= _BV(row_pins[r] & 0xF); -// } -// #endif -// } - void matrix_init(void) { // initialize row and col @@ -158,7 +121,6 @@ void matrix_init(void) { // initialize matrix state: all keys off for (uint8_t i=0; i < MATRIX_ROWS; i++) { matrix[i] = 0; - matrix_debouncing[i] = 0; } matrix_init_quantum(); @@ -168,59 +130,24 @@ uint8_t matrix_scan(void) { #if (DIODE_DIRECTION == COL2ROW) - // Set row, read cols for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) { -# if (DEBOUNCING_DELAY > 0) - bool matrix_changed = read_cols_on_row(matrix_debouncing, current_row); - - if (matrix_changed) { - debouncing = true; - debouncing_time = timer_read(); - } - -# else - read_cols_on_row(matrix, current_row); -# endif - + read_cols_on_row(matrix, current_row); } - #elif (DIODE_DIRECTION == ROW2COL) - // Set col, read rows for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { -# if (DEBOUNCING_DELAY > 0) - bool matrix_changed = read_rows_on_col(matrix_debouncing, current_col); - if (matrix_changed) { - debouncing = true; - debouncing_time = timer_read(); - } -# else - read_rows_on_col(matrix, current_col); -# endif - + read_rows_on_col(matrix, current_col); } - #endif -# if (DEBOUNCING_DELAY > 0) - if (debouncing && (timer_elapsed(debouncing_time) > DEBOUNCING_DELAY)) { - for (uint8_t i = 0; i < MATRIX_ROWS; i++) { - matrix[i] = matrix_debouncing[i]; - } - debouncing = false; - } -# endif - matrix_scan_quantum(); return 1; } +//Deprecated. bool matrix_is_modified(void) { -#if (DEBOUNCING_DELAY > 0) - if (debouncing) return false; -#endif return true; } diff --git a/tmk_core/common.mk b/tmk_core/common.mk index 063115acb1..7a7b3928f4 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -1,4 +1,5 @@ COMMON_DIR = common +DEBOUNCE = $(COMMON_DIR)/debounce ifeq ($(PLATFORM),AVR) PLATFORM_COMMON_DIR = $(COMMON_DIR)/avr else ifeq ($(PLATFORM),CHIBIOS) @@ -11,6 +12,7 @@ endif TMK_COMMON_SRC += $(COMMON_DIR)/host.c \ $(COMMON_DIR)/keyboard.c \ + $(COMMON_DIR)/debounce.c \ $(COMMON_DIR)/action.c \ $(COMMON_DIR)/action_tapping.c \ $(COMMON_DIR)/action_macro.c \ @@ -60,7 +62,20 @@ ifeq ($(PLATFORM),TEST) TMK_COMMON_SRC += $(PLATFORM_COMMON_DIR)/eeprom.c endif - +# Debounce Modules. If implemented in matrix.c, don't use these. +ifeq ($(strip $(SPLIT_KEYBOARD)), yes) + # Do nothing, debouncing is inside matrix.c inside split_common +else ifeq ($(strip $(DEBOUNCE_ALGO)), manual) + # Do nothing. do your debouncing in matrix.c +else ifeq ($(strip $(DEBOUNCE_ALGO)), sym_g) + TMK_COMMON_SRC += $(DEBOUNCE)/debounce_sym_g.c +else ifeq ($(strip $(DEBOUNCE_ALGO)), eager_pk) + TMK_COMMON_SRC += $(DEBOUNCE)/debounce_eager_pk.c +else ifeq ($(strip $(CUSTOM_MATRIX)), yes) + # Do nothing. Custom matrix code. +else # default algorithm + TMK_COMMON_SRC += $(DEBOUNCE)/debounce_sym_g.c +endif # Option modules BOOTMAGIC_ENABLE ?= no diff --git a/tmk_core/common/debounce.c b/tmk_core/common/debounce.c new file mode 100644 index 0000000000..406d2d0f38 --- /dev/null +++ b/tmk_core/common/debounce.c @@ -0,0 +1,22 @@ +/* +Copyright 2017 Alex Ong<the.onga@gmail.com> +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 <http://www.gnu.org/licenses/>. +*/ +#include "debounce.h" +#include "matrix.h" +//Default implementation - no debouncing +__attribute__((weak)) void matrix_debounce_init(void) {} +__attribute__((weak)) void matrix_debounce(void) {} +__attribute__((weak)) matrix_row_t matrix_debounce_get_row(uint8_t row) +{ + return matrix_get_row(row); +} diff --git a/tmk_core/common/debounce.h b/tmk_core/common/debounce.h new file mode 100644 index 0000000000..fe3effab32 --- /dev/null +++ b/tmk_core/common/debounce.h @@ -0,0 +1,21 @@ +#ifndef DEBOUNCE_H +#define DEBOUNCE_H +#include <stdbool.h> +#include <stdint.h> +#include "matrix.h" +#ifdef __cplusplus +extern "C" { +#endif + /* called to initialize any data stores your implementation has*/ + void matrix_debounce_init(void); + /* call this every keyboard_task to debounce the matrix*/ + void matrix_debounce(void); + /* matrix state on row */ + matrix_row_t matrix_debounce_get_row(uint8_t row); + /* whether a switch is on */ + bool matrix_debounce_is_on(uint8_t row, uint8_t col); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/tmk_core/common/debounce/debounce_eager_pk.c b/tmk_core/common/debounce/debounce_eager_pk.c new file mode 100644 index 0000000000..984da0570a --- /dev/null +++ b/tmk_core/common/debounce/debounce_eager_pk.c @@ -0,0 +1,124 @@ +/* +Copyright 2017 Alex Ong<the.onga@gmail.com> +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 <http://www.gnu.org/licenses/>. +*/ + +/* +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 "debounce.h" +#include "matrix.h" +#include "timer.h" + +#ifndef DEBOUNCE + #define DEBOUNCE 5 +#endif + + +#if (MATRIX_COLS <= 8) +# define ROW_SHIFTER ((uint8_t)1) +#elif (MATRIX_COLS <= 16) +# define ROW_SHIFTER ((uint16_t)1) +#elif (MATRIX_COLS <= 32) +# define ROW_SHIFTER ((uint32_t)1) +#endif + + + +#define debounce_counter_t uint8_t + +static matrix_row_t matrix_debounced[MATRIX_ROWS]; +static debounce_counter_t debounce_counters[MATRIX_ROWS*MATRIX_COLS]; + +#define DEBOUNCE_ELAPSED 251 +#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) + +void update_debounce_counters(uint8_t current_time); +void transfer_matrix_values(uint8_t current_time); + +void matrix_debounce_init(void) +{ + for (uint8_t r = 0; r < MATRIX_ROWS; r++) + { + matrix_debounced[r] = 0; + } + + int i = 0; + for (uint8_t r = 0; r < MATRIX_ROWS; r++) + { + for (uint8_t c = 0; c < MATRIX_COLS; c++) + { + debounce_counters[i++] = DEBOUNCE_ELAPSED; + } + } +} + +void matrix_debounce(void) +{ + uint8_t current_time = timer_read() % MAX_DEBOUNCE; + update_debounce_counters(current_time); + transfer_matrix_values(current_time); +} + +//If the current time is > debounce counter, set the counter to enable input. +void update_debounce_counters(uint8_t current_time) +{ + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < MATRIX_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) >= + DEBOUNCING_DELAY) { + *debounce_pointer = DEBOUNCE_ELAPSED; + } + } + debounce_pointer++; + } + } +} + +// upload from raw_matrix to final matrix; +void transfer_matrix_values(uint8_t current_time) +{ + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < MATRIX_ROWS; row++) + { + matrix_row_t existing_row = matrix_debounced[row]; + matrix_row_t raw_row = matrix_get_row(row); + + for (uint8_t col = 0; col < MATRIX_COLS; col++) + { + matrix_row_t col_mask = (ROW_SHIFTER << col); + bool final_value = raw_row & col_mask; + bool existing_value = existing_row & col_mask; + if (*debounce_pointer == DEBOUNCE_ELAPSED && + (existing_value != final_value)) + { + *debounce_pointer = current_time; + existing_row ^= col_mask; //flip the bit. + } + debounce_pointer++; + } + matrix_debounced[row] = existing_row; + } +} + +matrix_row_t matrix_debounce_get_row(uint8_t row) +{ + return matrix_debounced[row]; +} diff --git a/tmk_core/common/debounce/debounce_sym_g.c b/tmk_core/common/debounce/debounce_sym_g.c new file mode 100644 index 0000000000..2fb7a589d7 --- /dev/null +++ b/tmk_core/common/debounce/debounce_sym_g.c @@ -0,0 +1,59 @@ +/* +Copyright 2017 Alex Ong<the.onga@gmail.com> +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 <http://www.gnu.org/licenses/>. +*/ + +/* +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 "debounce.h" +#include "matrix.h" +#include "timer.h" +#ifndef DEBOUNCE + #define DEBOUNCE 5 +#endif +static matrix_row_t matrix_debounced[MATRIX_ROWS]; +static bool debouncing = false; +static uint16_t debouncing_time; + +void matrix_debounce_init(void) +{ + for (uint8_t r = 0; r < MATRIX_ROWS; r++) + { + matrix_debounced[r] = 0; + } +} + +void matrix_debounce(void) +{ + for (uint8_t r = 0; r < MATRIX_ROWS; r++) + { + matrix_row_t raw = matrix_get_row(r); + if (raw != matrix_debounced[r]) + { + debouncing = true; + debouncing_time = timer_read(); + } + } + if (debouncing && timer_elapsed(debouncing_time) > DEBOUNCE) { + for (int i = 0; i < MATRIX_ROWS; i++) { + matrix_debounced[i] = matrix_get_row(i); + } + debouncing = false; + } +} + +matrix_row_t matrix_debounce_get_row(uint8_t row) +{ + return matrix_debounced[row]; +} diff --git a/tmk_core/common/debounce/readme.md b/tmk_core/common/debounce/readme.md new file mode 100644 index 0000000000..1a77d44df5 --- /dev/null +++ b/tmk_core/common/debounce/readme.md @@ -0,0 +1,28 @@ +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 assymetric + * 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 + * Assymetric - 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: + +debounce_sym_g.c +debounce_sym_pk.c +debounce_sym_pr.c +debounce_sym_pr_cycles.c //currently used in ergo-dox +debounce_eager_g.c +debounce_eager_pk.c +debounce_eager_pr.c //could be used in ergo-dox! diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index 6f659b2440..1bfd4c9cca 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include <stdint.h> #include "keyboard.h" #include "matrix.h" +#include "debounce.h" #include "keymap.h" #include "host.h" #include "led.h" @@ -164,6 +165,7 @@ bool is_keyboard_master(void) { void keyboard_init(void) { timer_init(); matrix_init(); + matrix_debounce_init(); #ifdef QWIIC_ENABLE qwiic_init(); #endif @@ -216,9 +218,6 @@ void keyboard_init(void) { void keyboard_task(void) { static matrix_row_t matrix_prev[MATRIX_ROWS]; -#ifdef MATRIX_HAS_GHOST - // static matrix_row_t matrix_ghost[MATRIX_ROWS]; -#endif static uint8_t led_status = 0; matrix_row_t matrix_row = 0; matrix_row_t matrix_change = 0; @@ -227,24 +226,15 @@ void keyboard_task(void) #endif matrix_scan(); + matrix_debounce(); + if (is_keyboard_master()) { for (uint8_t r = 0; r < MATRIX_ROWS; r++) { - matrix_row = matrix_get_row(r); + matrix_row = matrix_debounce_get_row(r); matrix_change = matrix_row ^ matrix_prev[r]; if (matrix_change) { #ifdef MATRIX_HAS_GHOST - if (has_ghost_in_row(r, matrix_row)) { - /* Keep track of whether ghosted status has changed for - * debugging. But don't update matrix_prev until un-ghosted, or - * the last key would be lost. - */ - //if (debug_matrix && matrix_ghost[r] != matrix_row) { - // matrix_print(); - //} - //matrix_ghost[r] = matrix_row; - continue; - } - //matrix_ghost[r] = matrix_row; + if (has_ghost_in_row(r, matrix_row)) continue; #endif if (debug_matrix) matrix_print(); for (uint8_t c = 0; c < MATRIX_COLS; c++) { |