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.
pull/5613/head
Alex Ong 6 years ago committed by Drashna Jaelre
parent fd770232d9
commit 5747449ca5
No known key found for this signature in database
GPG Key ID: 4C4221222CD5F9F0

@ -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,61 +72,38 @@ 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))
void matrix_init_kb(void) {
matrix_init_user();
}
__attribute__ ((weak)) __attribute__((weak)) void matrix_scan_kb(void) { matrix_scan_user(); }
void matrix_scan_kb(void) {
matrix_scan_user();
}
inline inline uint8_t matrix_rows(void) { return MATRIX_ROWS; }
uint8_t matrix_rows(void)
{
return MATRIX_ROWS;
}
inline inline uint8_t matrix_cols(void) { return MATRIX_COLS; }
uint8_t matrix_cols(void)
{
return MATRIX_COLS;
}
void matrix_init(void) 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();
// initialize matrix state: all keys off // initialize matrix state: all keys off
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) {
@ -145,7 +113,7 @@ void matrix_power_up(void) {
init_cols(); init_cols();
// initialize matrix state: all keys off // initialize matrix state: all keys off
for (uint8_t i=0; i < MATRIX_ROWS; i++) { for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
matrix[i] = 0; matrix[i] = 0;
} }
@ -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) {
@ -205,7 +144,7 @@ uint8_t matrix_scan(void)
matrix_scan_count++; matrix_scan_count++;
uint32_t timer_now = timer_read32(); uint32_t timer_now = timer_read32();
if (TIMER_DIFF_32(timer_now, matrix_timer)>1000) { if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) {
print("matrix scan frequency: "); print("matrix scan frequency: ");
pdec(matrix_scan_count); pdec(matrix_scan_count);
print("\n"); print("\n");
@ -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,28 +215,30 @@ 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()
// init on teensy // init on teensy
// Input with pull-up(DDR:0, PORT:1) // Input with pull-up(DDR:0, PORT:1)
DDRF &= ~(1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<1 | 1<<0); DDRF &= ~(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); 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,24 +266,22 @@ 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
// unselect on teensy // unselect on teensy
// Hi-Z(DDR:0, PORT:0) to unselect // Hi-Z(DDR:0, PORT:0) to unselect
DDRB &= ~(1<<0 | 1<<1 | 1<<2 | 1<<3); DDRB &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3);
PORTB &= ~(1<<0 | 1<<1 | 1<<2 | 1<<3); PORTB &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3);
DDRD &= ~(1<<2 | 1<<3); DDRD &= ~(1 << 2 | 1 << 3);
PORTD &= ~(1<<2 | 1<<3); PORTD &= ~(1 << 2 | 1 << 3);
DDRC &= ~(1<<6); DDRC &= ~(1 << 6);
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();
} }
@ -369,32 +303,32 @@ static void select_row(uint8_t row)
// Output low(DDR:1, PORT:0) to select // Output low(DDR:1, PORT:0) to select
switch (row) { switch (row) {
case 7: case 7:
DDRB |= (1<<0); DDRB |= (1 << 0);
PORTB &= ~(1<<0); PORTB &= ~(1 << 0);
break; break;
case 8: case 8:
DDRB |= (1<<1); DDRB |= (1 << 1);
PORTB &= ~(1<<1); PORTB &= ~(1 << 1);
break; break;
case 9: case 9:
DDRB |= (1<<2); DDRB |= (1 << 2);
PORTB &= ~(1<<2); PORTB &= ~(1 << 2);
break; break;
case 10: case 10:
DDRB |= (1<<3); DDRB |= (1 << 3);
PORTB &= ~(1<<3); PORTB &= ~(1 << 3);
break; break;
case 11: case 11:
DDRD |= (1<<2); DDRD |= (1 << 2);
PORTD &= ~(1<<2); PORTD &= ~(1 << 2);
break; break;
case 12: case 12:
DDRD |= (1<<3); DDRD |= (1 << 3);
PORTD &= ~(1<<3); PORTD &= ~(1 << 3);
break; break;
case 13: case 13:
DDRC |= (1<<6); DDRC |= (1 << 6);
PORTC &= ~(1<<6); PORTC &= ~(1 << 6);
break; break;
} }
} }

@ -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

@ -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;
}

@ -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!

Loading…
Cancel
Save