Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid 8-bit timer overflows in debounce algorithms #12240

Merged
merged 6 commits into from
Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build_test.mk
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ endif

include common_features.mk
include $(TMK_PATH)/common.mk
include $(QUANTUM_PATH)/debounce/tests/rules.mk
zvecr marked this conversation as resolved.
Show resolved Hide resolved
include $(QUANTUM_PATH)/sequencer/tests/rules.mk
include $(QUANTUM_PATH)/serial_link/tests/rules.mk
ifneq ($(filter $(FULL_TESTS),$(TEST)),)
Expand Down
2 changes: 2 additions & 0 deletions quantum/debounce.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
bool debounce_active(void);

void debounce_init(uint8_t num_rows);

void debounce_free(void);
31 changes: 31 additions & 0 deletions quantum/debounce/none.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* Copyright 2021 Simon Arlott
*
* 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 "matrix.h"
#include "quantum.h"
#include <stdlib.h>

void debounce_init(uint8_t num_rows) {}

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

bool debounce_active(void) { return false; }

void debounce_free(void) {}
26 changes: 13 additions & 13 deletions quantum/debounce/sym_defer_g.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
Copyright 2017 Alex Ong<the.onga@gmail.com>
Copyright 2021 Simon Arlott
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
Expand All @@ -23,30 +24,29 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
# define DEBOUNCE 5
#endif

void debounce_init(uint8_t num_rows) {}
#if DEBOUNCE > 0
static bool debouncing = false;
static fast_timer_t debouncing_time;

#if DEBOUNCE > 0
static uint16_t debouncing_time;
void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
void debounce_init(uint8_t num_rows) {}

void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
if (changed) {
debouncing = true;
debouncing_time = timer_read();
debouncing_time = timer_read_fast();
}

if (debouncing && timer_elapsed(debouncing_time) > DEBOUNCE) {
if (debouncing && timer_elapsed_fast(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; }

void debounce_free(void) {}
#else // no debouncing.
# include "none.c"
#endif
67 changes: 45 additions & 22 deletions quantum/debounce/sym_defer_pk.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
Copyright 2017 Alex Ong<the.onga@gmail.com>
Copyright 2020 Andrei Purdea<andrei@purdea.ro>
Copyright 2021 Simon Arlott
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
Expand Down Expand Up @@ -33,28 +34,25 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
# define DEBOUNCE 5
#endif

// Maximum debounce: 255ms
#if DEBOUNCE > UINT8_MAX
# undef DEBOUNCE
# define DEBOUNCE UINT8_MAX
#endif

#define ROW_SHIFTER ((matrix_row_t)1)

#define debounce_counter_t uint8_t
typedef uint8_t debounce_counter_t;

#if DEBOUNCE > 0
static debounce_counter_t *debounce_counters;
static fast_timer_t last_time;
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;
}
#define DEBOUNCE_ELAPSED 0

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);
static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);

// we use num_rows rather than MATRIX_ROWS to support split keyboards
void debounce_init(uint8_t num_rows) {
Expand All @@ -67,27 +65,49 @@ void debounce_init(uint8_t num_rows) {
}
}

void debounce_free(void) {
free(debounce_counters);
debounce_counters = NULL;
}

void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
uint8_t current_time = wrapping_timer_read();
bool updated_last = false;

if (counters_need_update) {
update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, current_time);
fast_timer_t now = timer_read_fast();
fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);

last_time = now;
updated_last = true;
if (elapsed_time > UINT8_MAX) {
elapsed_time = UINT8_MAX;
}

if (elapsed_time > 0) {
update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time);
}
}

if (changed) {
start_debounce_counters(raw, cooked, num_rows, current_time);
if (!updated_last) {
last_time = timer_read_fast();
}

start_debounce_counters(raw, cooked, num_rows);
}
}

void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) {
static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) {
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) {
if (*debounce_pointer <= elapsed_time) {
*debounce_pointer = DEBOUNCE_ELAPSED;
cooked[row] = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col));
} else {
*debounce_pointer -= elapsed_time;
counters_need_update = true;
}
}
Expand All @@ -96,14 +116,14 @@ void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix
}
}

void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) {
static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
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;
*debounce_pointer = DEBOUNCE;
counters_need_update = true;
}
} else {
Expand All @@ -115,3 +135,6 @@ void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t
}

bool debounce_active(void) { return true; }
#else
# include "none.c"
#endif
72 changes: 47 additions & 25 deletions quantum/debounce/sym_eager_pk.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
Copyright 2017 Alex Ong<the.onga@gmail.com>
Copyright 2021 Simon Arlott
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
Expand Down Expand Up @@ -33,29 +34,26 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
# define DEBOUNCE 5
#endif

// Maximum debounce: 255ms
#if DEBOUNCE > UINT8_MAX
# undef DEBOUNCE
# define DEBOUNCE UINT8_MAX
#endif

#define ROW_SHIFTER ((matrix_row_t)1)

#define debounce_counter_t uint8_t
typedef uint8_t debounce_counter_t;

#if DEBOUNCE > 0
static debounce_counter_t *debounce_counters;
static fast_timer_t last_time;
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;
}
#define DEBOUNCE_ELAPSED 0

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);
static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);

// we use num_rows rather than MATRIX_ROWS to support split keyboards
void debounce_init(uint8_t num_rows) {
Expand All @@ -68,27 +66,51 @@ void debounce_init(uint8_t num_rows) {
}
}

void debounce_free(void) {
free(debounce_counters);
debounce_counters = NULL;
}

void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
uint8_t current_time = wrapping_timer_read();
bool updated_last = false;

if (counters_need_update) {
update_debounce_counters(num_rows, current_time);
fast_timer_t now = timer_read_fast();
fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);

last_time = now;
updated_last = true;
if (elapsed_time > UINT8_MAX) {
elapsed_time = UINT8_MAX;
}

if (elapsed_time > 0) {
update_debounce_counters(num_rows, elapsed_time);
}
}

if (changed || matrix_need_update) {
transfer_matrix_values(raw, cooked, num_rows, current_time);
if (!updated_last) {
last_time = timer_read_fast();
}

transfer_matrix_values(raw, cooked, num_rows);
}
}

// 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) {
static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {
counters_need_update = false;
matrix_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) {
if (*debounce_pointer <= elapsed_time) {
*debounce_pointer = DEBOUNCE_ELAPSED;
matrix_need_update = true;
} else {
*debounce_pointer -= elapsed_time;
counters_need_update = true;
}
}
Expand All @@ -98,8 +120,7 @@ void update_debounce_counters(uint8_t num_rows, uint8_t current_time) {
}

// 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;
static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
debounce_counter_t *debounce_pointer = debounce_counters;
for (uint8_t row = 0; row < num_rows; row++) {
matrix_row_t delta = raw[row] ^ cooked[row];
Expand All @@ -108,11 +129,9 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n
matrix_row_t col_mask = (ROW_SHIFTER << col);
if (delta & col_mask) {
if (*debounce_pointer == DEBOUNCE_ELAPSED) {
*debounce_pointer = current_time;
*debounce_pointer = DEBOUNCE;
counters_need_update = true;
existing_row ^= col_mask; // flip the bit.
} else {
matrix_need_update = true;
}
}
debounce_pointer++;
Expand All @@ -122,3 +141,6 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n
}

bool debounce_active(void) { return true; }
#else
# include "none.c"
#endif
Loading