Eager Per Row Debouncing added (added to Ergodox) (#5498)

* Implemented Eager Per Row debouncing algorithm.

Good for when fingers can only press one row at a time (e.g. when keyboard is wired so that "rows" are vertical)

* Added documentation for eager_pr

* Ported ergodox_ez to eager_pr debouncing.

* Removed check for changes in matrix_scan.

* Added further clarification in docs.

* Accidental merge with ergodox_ez

* Small cleanup in eager_pr

* Forgot to debounce_init - this would probably cause seg-faults.
This commit is contained in:
Alex Ong 2019-04-04 08:45:55 +11:00 committed by Drashna Jaelre
parent e1e08a494b
commit 17e7762de7
5 changed files with 300 additions and 262 deletions

View File

@ -33,7 +33,10 @@ The debounce code is compatible with split keyboards.
# Changing between included debouncing methods # Changing between included debouncing methods
You can either use your own code, by including your own debounce.c, or switch to another included one. You can either use your own code, by including your own debounce.c, or switch to another included one.
Included debounce methods are: Included debounce methods are:
* eager_pk - debouncing per key. On any state change, response is immediate, followed by ```DEBOUNCE_DELAY``` millseconds of no further input for that key * eager_pr - debouncing per row. On any state change, response is immediate, followed by locking the row ```DEBOUNCE_DELAY``` 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_DELAY``` milliseconds of no further input for that key
* 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. * 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.

View File

@ -33,14 +33,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "debug.h" #include "debug.h"
#include "util.h" #include "util.h"
#include "matrix.h" #include "matrix.h"
#include "debounce.h"
#include QMK_KEYBOARD_H #include QMK_KEYBOARD_H
#ifdef DEBUG_MATRIX_SCAN_RATE #ifdef DEBUG_MATRIX_SCAN_RATE
# include "timer.h" # include "timer.h"
#endif #endif
/* /*
* This constant define not debouncing time in msecs, but amount of matrix * This constant define not debouncing time in msecs, assuming eager_pr.
* scan loops which should be made to get stable debounced results.
* *
* On Ergodox matrix scan rate is relatively low, because of slow I2C. * On Ergodox matrix scan rate is relatively low, because of slow I2C.
* Now it's only 317 scans/second, or about 3.15 msec/scan. * Now it's only 317 scans/second, or about 3.15 msec/scan.
@ -56,17 +56,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#endif #endif
/* matrix state(1:on, 0:off) */ /* matrix state(1:on, 0:off) */
static matrix_row_t matrix[MATRIX_ROWS]; static matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
/* static matrix_row_t matrix[MATRIX_ROWS]; // debounced values
* matrix state(1:on, 0:off)
* contains the raw values without debounce filtering of the last read cycle.
*/
static matrix_row_t raw_matrix[MATRIX_ROWS];
// Debouncing: store for each key the number of scans until it's eligible to
// change. When scanning the matrix, ignore any changes in keys that have
// already changed in the last DEBOUNCE scans.
static uint8_t debounce_matrix[MATRIX_ROWS * MATRIX_COLS];
static matrix_row_t read_cols(uint8_t row); static matrix_row_t read_cols(uint8_t row);
static void init_cols(void); static void init_cols(void);
@ -81,42 +72,23 @@ uint32_t matrix_timer;
uint32_t matrix_scan_count; uint32_t matrix_scan_count;
#endif #endif
__attribute__((weak)) void matrix_init_user(void) {}
__attribute__ ((weak)) __attribute__((weak)) void matrix_scan_user(void) {}
void matrix_init_user(void) {}
__attribute__ ((weak)) __attribute__((weak)) void matrix_init_kb(void) { matrix_init_user(); }
void matrix_scan_user(void) {}
__attribute__ ((weak)) __attribute__((weak)) void matrix_scan_kb(void) { matrix_scan_user(); }
void matrix_init_kb(void) {
matrix_init_user();
}
__attribute__ ((weak)) inline uint8_t matrix_rows(void) { return MATRIX_ROWS; }
void matrix_scan_kb(void) {
matrix_scan_user();
}
inline inline uint8_t matrix_cols(void) { return MATRIX_COLS; }
uint8_t matrix_rows(void)
{
return MATRIX_ROWS;
}
inline void matrix_init(void) {
uint8_t matrix_cols(void)
{
return MATRIX_COLS;
}
void matrix_init(void)
{
// initialize row and col // initialize row and col
mcp23018_status = init_mcp23018(); mcp23018_status = init_mcp23018();
unselect_rows(); unselect_rows();
init_cols(); init_cols();
@ -124,18 +96,14 @@ void matrix_init(void)
for (uint8_t i = 0; i < MATRIX_ROWS; i++) { for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
matrix[i] = 0; matrix[i] = 0;
raw_matrix[i] = 0; raw_matrix[i] = 0;
for (uint8_t j=0; j < MATRIX_COLS; ++j) {
debounce_matrix[i * MATRIX_COLS + j] = 0;
}
} }
#ifdef DEBUG_MATRIX_SCAN_RATE #ifdef DEBUG_MATRIX_SCAN_RATE
matrix_timer = timer_read32(); matrix_timer = timer_read32();
matrix_scan_count = 0; matrix_scan_count = 0;
#endif #endif
debounce_init(MATRIX_ROWS);
matrix_init_quantum(); matrix_init_quantum();
} }
void matrix_power_up(void) { void matrix_power_up(void) {
@ -155,36 +123,7 @@ void matrix_power_up(void) {
#endif #endif
} }
// Returns a matrix_row_t whose bits are set if the corresponding key should be uint8_t matrix_scan(void) {
// eligible to change in this scan.
matrix_row_t debounce_mask(matrix_row_t rawcols, uint8_t row) {
matrix_row_t result = 0;
matrix_row_t change = rawcols ^ raw_matrix[row];
raw_matrix[row] = rawcols;
for (uint8_t i = 0; i < MATRIX_COLS; ++i) {
if (debounce_matrix[row * MATRIX_COLS + i]) {
--debounce_matrix[row * MATRIX_COLS + i];
} else {
result |= (1 << i);
}
if (change & (1 << i)) {
debounce_matrix[row * MATRIX_COLS + i] = DEBOUNCE;
}
}
return result;
}
matrix_row_t debounce_read_cols(uint8_t row) {
// Read the row without debouncing filtering and store it for later usage.
matrix_row_t cols = read_cols(row);
// Get the Debounce mask.
matrix_row_t mask = debounce_mask(cols, row);
// debounce the row and return the result.
return (cols & mask) | (matrix[row] & ~mask);;
}
uint8_t matrix_scan(void)
{
if (mcp23018_status) { // if there was an error if (mcp23018_status) { // if there was an error
if (++mcp23018_reset_loop == 0) { if (++mcp23018_reset_loop == 0) {
// if (++mcp23018_reset_loop >= 1300) { // if (++mcp23018_reset_loop >= 1300) {
@ -219,20 +158,21 @@ uint8_t matrix_scan(void)
mcp23018_status = ergodox_left_leds_update(); mcp23018_status = ergodox_left_leds_update();
#endif // LEFT_LEDS #endif // LEFT_LEDS
for (uint8_t i = 0; i < MATRIX_ROWS_PER_SIDE; i++) { for (uint8_t i = 0; i < MATRIX_ROWS_PER_SIDE; i++) {
// select rows from left and right hands
select_row(i); select_row(i);
// and select on left hand
select_row(i + MATRIX_ROWS_PER_SIDE); select_row(i + MATRIX_ROWS_PER_SIDE);
// we don't need a 30us delay anymore, because selecting a // we don't need a 30us delay anymore, because selecting a
// left-hand row requires more than 30us for i2c. // left-hand row requires more than 30us for i2c.
// grab cols from left hand // grab left + right cols.
matrix[i] = debounce_read_cols(i); raw_matrix[i] = read_cols(i);
// grab cols from right hand raw_matrix[i+MATRIX_ROWS_PER_SIDE] = read_cols(i+MATRIX_ROWS_PER_SIDE);
matrix[i + MATRIX_ROWS_PER_SIDE] = debounce_read_cols(i + MATRIX_ROWS_PER_SIDE);
unselect_rows(); unselect_rows();
} }
debounce(raw_matrix, matrix, MATRIX_ROWS, true);
matrix_scan_quantum(); matrix_scan_quantum();
return 1; return 1;
@ -243,30 +183,21 @@ bool matrix_is_modified(void) // deprecated and evidently not called.
return true; return true;
} }
inline inline bool matrix_is_on(uint8_t row, uint8_t col) { return (matrix[row] & ((matrix_row_t)1 << col)); }
bool matrix_is_on(uint8_t row, uint8_t col)
{
return (matrix[row] & ((matrix_row_t)1<<col));
}
inline inline matrix_row_t matrix_get_row(uint8_t row) { return matrix[row]; }
matrix_row_t matrix_get_row(uint8_t row)
{
return matrix[row];
}
void matrix_print(void) void matrix_print(void) {
{
print("\nr/c 0123456789ABCDEF\n"); print("\nr/c 0123456789ABCDEF\n");
for (uint8_t row = 0; row < MATRIX_ROWS; row++) { for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
phex(row); print(": "); phex(row);
print(": ");
pbin_reverse16(matrix_get_row(row)); pbin_reverse16(matrix_get_row(row));
print("\n"); print("\n");
} }
} }
uint8_t matrix_key_count(void) uint8_t matrix_key_count(void) {
{
uint8_t count = 0; uint8_t count = 0;
for (uint8_t i = 0; i < MATRIX_ROWS; i++) { for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
count += bitpop16(matrix[i]); count += bitpop16(matrix[i]);
@ -284,8 +215,7 @@ uint8_t matrix_key_count(void)
* col: 0 1 2 3 4 5 * col: 0 1 2 3 4 5
* pin: B5 B4 B3 B2 B1 B0 * pin: B5 B4 B3 B2 B1 B0
*/ */
static void init_cols(void) static void init_cols(void) {
{
// init on mcp23018 // init on mcp23018
// not needed, already done as part of init_mcp23018() // not needed, already done as part of init_mcp23018()
@ -295,17 +225,20 @@ static void init_cols(void)
PORTF |= (1 << 7 | 1 << 6 | 1 << 5 | 1 << 4 | 1 << 1 | 1 << 0); PORTF |= (1 << 7 | 1 << 6 | 1 << 5 | 1 << 4 | 1 << 1 | 1 << 0);
} }
static matrix_row_t read_cols(uint8_t row) static matrix_row_t read_cols(uint8_t row) {
{
if (row < 7) { if (row < 7) {
if (mcp23018_status) { // if there was an error if (mcp23018_status) { // if there was an error
return 0; return 0;
} else { } else {
uint8_t data = 0; uint8_t data = 0;
mcp23018_status = i2c_start(I2C_ADDR_WRITE, ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out; mcp23018_status = i2c_start(I2C_ADDR_WRITE, ERGODOX_EZ_I2C_TIMEOUT);
mcp23018_status = i2c_write(GPIOB, ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out; if (mcp23018_status) goto out;
mcp23018_status = i2c_start(I2C_ADDR_READ, ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out; mcp23018_status = i2c_write(GPIOB, ERGODOX_EZ_I2C_TIMEOUT);
mcp23018_status = i2c_read_nack(ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status < 0) goto out; if (mcp23018_status) goto out;
mcp23018_status = i2c_start(I2C_ADDR_READ, ERGODOX_EZ_I2C_TIMEOUT);
if (mcp23018_status) goto out;
mcp23018_status = i2c_read_nack(ERGODOX_EZ_I2C_TIMEOUT);
if (mcp23018_status < 0) goto out;
data = ~((uint8_t)mcp23018_status); data = ~((uint8_t)mcp23018_status);
mcp23018_status = I2C_STATUS_SUCCESS; mcp23018_status = I2C_STATUS_SUCCESS;
out: out:
@ -333,8 +266,7 @@ static matrix_row_t read_cols(uint8_t row)
* row: 0 1 2 3 4 5 6 * row: 0 1 2 3 4 5 6
* pin: A0 A1 A2 A3 A4 A5 A6 * pin: A0 A1 A2 A3 A4 A5 A6
*/ */
static void unselect_rows(void) static void unselect_rows(void) {
{
// no need to unselect on mcp23018, because the select step sets all // no need to unselect on mcp23018, because the select step sets all
// the other row bits high, and it's not changing to a different // the other row bits high, and it's not changing to a different
// direction // direction
@ -349,8 +281,7 @@ static void unselect_rows(void)
PORTC &= ~(1 << 6); PORTC &= ~(1 << 6);
} }
static void select_row(uint8_t row) static void select_row(uint8_t row) {
{
if (row < 7) { if (row < 7) {
// select on mcp23018 // select on mcp23018
if (mcp23018_status) { // if there was an error if (mcp23018_status) { // if there was an error
@ -358,9 +289,12 @@ static void select_row(uint8_t row)
} else { } else {
// set active row low : 0 // set active row low : 0
// set other rows hi-Z : 1 // set other rows hi-Z : 1
mcp23018_status = i2c_start(I2C_ADDR_WRITE, ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out; mcp23018_status = i2c_start(I2C_ADDR_WRITE, ERGODOX_EZ_I2C_TIMEOUT);
mcp23018_status = i2c_write(GPIOA, ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out; if (mcp23018_status) goto out;
mcp23018_status = i2c_write(0xFF & ~(1<<row), ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out; mcp23018_status = i2c_write(GPIOA, ERGODOX_EZ_I2C_TIMEOUT);
if (mcp23018_status) goto out;
mcp23018_status = i2c_write(0xFF & ~(1 << row), ERGODOX_EZ_I2C_TIMEOUT);
if (mcp23018_status) goto out;
out: out:
i2c_stop(); i2c_stop();
} }

View File

@ -83,6 +83,7 @@ SLEEP_LED_ENABLE = no
API_SYSEX_ENABLE = no API_SYSEX_ENABLE = no
RGBLIGHT_ENABLE = yes RGBLIGHT_ENABLE = yes
RGB_MATRIX_ENABLE = no # enable later RGB_MATRIX_ENABLE = no # enable later
DEBOUNCE_TYPE = eager_pr
ifeq ($(strip $(RGB_MATRIX_ENABLE)), no) ifeq ($(strip $(RGB_MATRIX_ENABLE)), no)
SRC += i2c_master.c SRC += i2c_master.c

100
quantum/debounce/eager_pr.c Normal file
View File

@ -0,0 +1,100 @@
/*
Copyright 2019 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-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 <stdlib.h>
#ifndef DEBOUNCE
#define DEBOUNCE 5
#endif
#define debounce_counter_t uint8_t
static debounce_counter_t *debounce_counters;
#define DEBOUNCE_ELAPSED 251
#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1)
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 = timer_read() % MAX_DEBOUNCE;
update_debounce_counters(num_rows, current_time);
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)
{
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;
}
}
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)
{
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 (*debounce_pointer == DEBOUNCE_ELAPSED &&
(existing_row != raw_row))
{
*debounce_pointer = current_time;
existing_row = raw_row;
}
cooked[row] = existing_row;
debounce_pointer++;
}
}
bool debounce_active(void)
{
return true;
}

View File

@ -22,7 +22,7 @@ Here are a few that could be implemented:
sym_g.c sym_g.c
sym_pk.c sym_pk.c
sym_pr.c sym_pr.c
sym_pr_cycles.c //currently used in ergo-dox sym_pr_cycles.c
eager_g.c eager_g.c
eager_pk.c eager_pk.c
eager_pr.c //could be used in ergo-dox! eager_pr.c //could be used in ergo-dox!