Added snap click, per-key/mixed rgb, custom debounce, wireless config feature

This commit is contained in:
lokher
2025-05-30 23:55:10 +08:00
parent b507ea2216
commit c9049679ac
81 changed files with 3696 additions and 251 deletions
@@ -0,0 +1,170 @@
/*
* Copyright 2017 Alex Ong <the.onga@gmail.com>
* Copyright 2020 Andrei Purdea <andrei@purdea.ro>
* Copyright 2021 Simon Arlott
* Copyright 2024 @ keychron (https://www.keychron.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 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 "debounce.h"
#include "timer.h"
#include <stdlib.h>
#ifdef PROTOCOL_CHIBIOS
# if CH_CFG_USE_MEMCORE == FALSE
# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
# endif
#endif
#define ROW_SHIFTER ((matrix_row_t)1)
typedef struct {
bool pressed : 1;
uint8_t time : 7;
} debounce_counter_t;
extern uint8_t debounce_time;
static debounce_counter_t *debounce_counters = NULL;
static fast_timer_t last_time;
static bool counters_need_update;
static bool matrix_need_update;
static bool cooked_changed;
# define DEBOUNCE_ELAPSED 0
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 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 asym_eager_defer_pk_debounce_init(uint8_t num_rows) {
debounce_counters = 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++].time = DEBOUNCE_ELAPSED;
}
}
}
void asym_eager_defer_pk_debounce_free(void) {
if (debounce_counters != NULL) {
free(debounce_counters);
debounce_counters = NULL;
}
}
bool asym_eager_defer_pk_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
bool updated_last = false;
cooked_changed = false;
if (counters_need_update) {
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 || matrix_need_update) {
if (!updated_last) {
last_time = timer_read_fast();
}
transfer_matrix_values(raw, cooked, num_rows);
}
return cooked_changed;
}
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) {
debounce_counter_t *debounce_pointer = debounce_counters;
counters_need_update = false;
matrix_need_update = false;
for (uint8_t row = 0; row < num_rows; row++) {
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
matrix_row_t col_mask = (ROW_SHIFTER << col);
if (debounce_pointer->time != DEBOUNCE_ELAPSED) {
if (debounce_pointer->time <= elapsed_time) {
debounce_pointer->time = DEBOUNCE_ELAPSED;
if (debounce_pointer->pressed) {
// key-down: eager
matrix_need_update = true;
} else {
// key-up: defer
matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask);
cooked_changed |= cooked_next ^ cooked[row];
cooked[row] = cooked_next;
}
} else {
debounce_pointer->time -= elapsed_time;
counters_need_update = true;
}
}
debounce_pointer++;
}
}
}
static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
debounce_counter_t *debounce_pointer = debounce_counters;
matrix_need_update = false;
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++) {
matrix_row_t col_mask = (ROW_SHIFTER << col);
if (delta & col_mask) {
if (debounce_pointer->time == DEBOUNCE_ELAPSED) {
debounce_pointer->pressed = (raw[row] & col_mask);
debounce_pointer->time = debounce_time;;
counters_need_update = true;
if (debounce_pointer->pressed) {
// key-down: eager
cooked[row] ^= col_mask;
cooked_changed = true;
}
}
} else if (debounce_pointer->time != DEBOUNCE_ELAPSED) {
if (!debounce_pointer->pressed) {
// key-up: defer
debounce_pointer->time = DEBOUNCE_ELAPSED;
}
}
debounce_pointer++;
}
}
}
@@ -0,0 +1,14 @@
DEBOUNCE_DIR = common/debounce
SRC += \
$(DEBOUNCE_DIR)/sym_defer_g.c \
$(DEBOUNCE_DIR)/sym_defer_pr.c \
$(DEBOUNCE_DIR)/sym_defer_pk.c \
$(DEBOUNCE_DIR)/sym_eager_pr.c \
$(DEBOUNCE_DIR)/sym_eager_pk.c \
$(DEBOUNCE_DIR)/asym_eager_defer_pk.c \
$(DEBOUNCE_DIR)/none.c \
$(DEBOUNCE_DIR)/keychron_debounce.c
VPATH += $(TOP_DIR)/keyboards/keychron/$(DEBOUNCE_DIR)
OPT_DEFS += -DDYNAMIC_DEBOUNCE_ENABLE
@@ -0,0 +1,20 @@
/* Copyright 2024 @ Keychron (https://www.keychron.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/>.
*/
#pragma once
#define EECONFIG_SIZE_DEBOUNCE 2
@@ -0,0 +1,214 @@
/* Copyright 2024 @ Keychron (https://www.keychron.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 "keychron_debounce.h"
#include "raw_hid.h"
#include "quantum.h"
#include "eeconfig.h"
#include "eeconfig_kb.h"
#include "keychron_raw_hid.h"
#ifdef SPLIT_KEYBOARD
# pragma(error "Split keyboard is not supported")
#endif
#ifndef DEBOUNCE
# define DEBOUNCE 5
#endif
// Maximum debounce: 255ms
#if DEBOUNCE > UINT8_MAX
# undef DEBOUNCE
# define DEBOUNCE UINT8_MAX
#endif
#ifndef DEFAULT_DEBOUNCE_TYPE
#define DEFAULT_DEBOUNCE_TYPE DEBOUNCE_SYM_EAGER_PER_KEY
#endif
#define DEBOUNCE_SET_QMK 0
#define OFFSET_DEBOUNCE ((uint8_t *)(EECONFIG_BASE_DYNAMIC_DEBOUNCE))
static uint8_t debounce_type = 0;
uint8_t debounce_time = 0;
static debounce_t debounce_func = {NULL, NULL, NULL};
extern void sym_defer_g_debounce_init(uint8_t num_rows);
extern bool sym_defer_g_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);
extern void sym_defer_g_debounce_free(void);
extern void sym_defer_pr_debounce_init(uint8_t num_rows);
extern bool sym_defer_pr_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);
extern void sym_defer_pr_debounce_free(void);
extern void sym_defer_pk_debounce_init(uint8_t num_rows);
extern bool sym_defer_pk_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);
extern void sym_defer_pk_debounce_free(void);
extern void sym_eager_pr_debounce_init(uint8_t num_rows);
extern bool sym_eager_pr_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);
extern void sym_eager_pr_debounce_free(void);
extern void sym_eager_pk_debounce_init(uint8_t num_rows);
extern bool sym_eager_pk_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);
extern void sym_eager_pk_debounce_free(void);
extern void asym_eager_defer_pk_debounce_init(uint8_t num_rows);
extern bool asym_eager_defer_pk_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);
extern void asym_eager_defer_pk_debounce_free(void);
extern void none_debounce_init(uint8_t num_rows);
extern bool none_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);
extern void none_debounce_free(void);
void debounce_set(uint8_t new_debounce_type, uint8_t time, bool force);
/**
* @brief Debounce raw matrix events according to the choosen debounce algorithm.
*
* @param raw The current key state
* @param cooked The debounced key state
* @param num_rows Number of rows to debounce
* @param changed True if raw has changed since the last call
* @return true Cooked has new keychanges after debouncing
* @return false Cooked is the same as before
*/
bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
if (debounce_func.debounce) debounce_func.debounce(raw, cooked, num_rows, changed);
return false;
}
void debounce_init(uint8_t num_rows) {
debounce_type = 0;
// debounce_set(DEBOUNCE_SYM_EAGER_PER_KEY, DEBOUNCE);
if (!eeconfig_is_enabled()) {
eeconfig_init();
}
uint8_t type = eeprom_read_byte(OFFSET_DEBOUNCE);
uint8_t time = eeprom_read_byte(OFFSET_DEBOUNCE + 1);
if (type >= DEBOUNCE_MAX) type = DEFAULT_DEBOUNCE_TYPE;
debounce_set(type, time, debounce_type == type);
}
void debounce_free(void) {
if (debounce_func.debounce_free) debounce_func.debounce_free();
}
static bool debounce_save(void) {
eeprom_update_byte(OFFSET_DEBOUNCE, debounce_type);
eeprom_update_byte(OFFSET_DEBOUNCE + 1, debounce_time);
return true;
}
void debounce_config_reset(void) {
debounce_set(DEFAULT_DEBOUNCE_TYPE, DEBOUNCE, true);
debounce_save();
}
void debounce_set(uint8_t new_debounce_type, uint8_t time, bool force) {
if (new_debounce_type == debounce_type && time == debounce_time && !force) return;
debounce_free();
debounce_type = new_debounce_type;
debounce_time = time;
if (debounce_time == 0) new_debounce_type = DEBOUNCE_NONE;
switch (new_debounce_type) {
case DEBOUNCE_SYM_DEFER_GLOBAL:
debounce_func.debounce_init = sym_defer_g_debounce_init;
debounce_func.debounce = sym_defer_g_debounce;
debounce_func.debounce_free = sym_defer_g_debounce_free;
break;
case DEBOUNCE_SYM_DEFER_PER_ROW:
debounce_func.debounce_init = sym_defer_pr_debounce_init;
debounce_func.debounce = sym_defer_pr_debounce;
debounce_func.debounce_free = sym_defer_pr_debounce_free;
break;
case DEBOUNCE_SYM_DEFER_PER_KEY:
debounce_func.debounce_init = sym_defer_pk_debounce_init;
debounce_func.debounce = sym_defer_pk_debounce;
debounce_func.debounce_free = sym_defer_pk_debounce_free;
break;
case DEBOUNCE_SYM_EAGER_PER_ROW:
debounce_func.debounce_init = sym_eager_pr_debounce_init;
debounce_func.debounce = sym_eager_pr_debounce;
debounce_func.debounce_free = sym_eager_pr_debounce_free;
break;
case DEBOUNCE_SYM_EAGER_PER_KEY:
debounce_func.debounce_init = sym_eager_pk_debounce_init;
debounce_func.debounce = sym_eager_pk_debounce;
debounce_func.debounce_free = sym_eager_pk_debounce_free;
break;
case DEBOUNCE_ASYM_EAGER_DEFER_PER_KEY:
debounce_func.debounce_init = asym_eager_defer_pk_debounce_init;
debounce_func.debounce = asym_eager_defer_pk_debounce;
debounce_func.debounce_free = asym_eager_defer_pk_debounce_free;
if (debounce_time > 127) debounce_time = 127;
break;
case DEBOUNCE_NONE:
debounce_func.debounce_init = none_debounce_init;
debounce_func.debounce = none_debounce;
debounce_func.debounce_free = none_debounce_free;
break;
}
if (debounce_func.debounce_init) debounce_func.debounce_init(MATRIX_ROWS);
}
void debounce_time_set(uint8_t time) {
debounce_time = time;
}
void debounce_rx(uint8_t *data, uint8_t length) {
uint8_t cmd = data[1];
switch (cmd) {
case DEBOUNCE_GET:
data[2] = 0;
data[3] = DEBOUNCE_SET_QMK;
data[4] = debounce_type;
data[5] = debounce_time;
break;
case DEBOUNCE_SET: {
uint8_t type = data[2];
uint8_t time = data[3];
if (type < DEBOUNCE_MAX) {
data[2] = 0;
debounce_set(type, time, false);
debounce_save();
} else
data[2] = 1;
} break;
default:
data[0] = 0xFF;
break;
}
}
@@ -0,0 +1,56 @@
/* Copyright 2024 @ Keychron (https://www.keychron.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/>.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "matrix.h"
enum {
DEBOUNCE_SYM_DEFER_GLOBAL,
DEBOUNCE_SYM_DEFER_PER_ROW,
DEBOUNCE_SYM_DEFER_PER_KEY,
DEBOUNCE_SYM_EAGER_PER_ROW,
DEBOUNCE_SYM_EAGER_PER_KEY,
DEBOUNCE_ASYM_EAGER_DEFER_PER_KEY,
DEBOUNCE_NONE,
DEBOUNCE_MAX,
};
typedef struct {
void (*debounce_init)(uint8_t);
bool (*debounce)(matrix_row_t [], matrix_row_t [], uint8_t, bool);
void (*debounce_free)(void);
} debounce_t;
/**
* @brief Debounce raw matrix events according to the choosen debounce algorithm.
*
* @param raw The current key state
* @param cooked The debounced key state
* @param num_rows Number of rows to debounce
* @param changed True if raw has changed since the last call
* @return true Cooked has new keychanges after debouncing
* @return false Cooked is the same as before
*/
bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);
void debounce_init(uint8_t num_rows);
void debounce_config_reset(void);
void debounce_free(void);
void debounce_rx(uint8_t *data, uint8_t length);
+36
View File
@@ -0,0 +1,36 @@
/* 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 "debounce.h"
#include <string.h>
void none_debounce_init(uint8_t num_rows) {}
bool none_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
bool cooked_changed = false;
if (changed) {
size_t matrix_size = num_rows * sizeof(matrix_row_t);
if (memcmp(cooked, raw, matrix_size) != 0) {
memcpy(cooked, raw, matrix_size);
cooked_changed = true;
}
}
return cooked_changed;
}
void none_debounce_free(void) {}
+36
View File
@@ -0,0 +1,36 @@
/* 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 "debounce.h"
#include <string.h>
void none_debounce_init(uint8_t num_rows) {}
bool none_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
bool cooked_changed = false;
if (changed) {
size_t matrix_size = num_rows * sizeof(matrix_row_t);
if (memcmp(cooked, raw, matrix_size) != 0) {
memcpy(cooked, raw, matrix_size);
cooked_changed = true;
}
}
return cooked_changed;
}
void none_debounce_free(void) {}
@@ -0,0 +1,51 @@
/*
Copyright 2017 Alex Ong<the.onga@gmail.com>
Copyright 2021 Simon Arlott
Copyright 2024 @ keychron (https://www.keychron.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 "timer.h"
#include <string.h>
extern uint8_t debounce_time;
static bool debouncing = false;
static fast_timer_t debouncing_time;
void sym_defer_g_debounce_init(uint8_t num_rows) {}
bool sym_defer_g_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
bool cooked_changed = false;
if (changed) {
debouncing = true;
debouncing_time = timer_read_fast();
} else if (debouncing && timer_elapsed_fast(debouncing_time) >= debounce_time) {
size_t matrix_size = num_rows * sizeof(matrix_row_t);
if (memcmp(cooked, raw, matrix_size) != 0) {
memcpy(cooked, raw, matrix_size);
cooked_changed = true;
}
debouncing = false;
}
return cooked_changed;
}
void sym_defer_g_debounce_free(void) {}
@@ -0,0 +1,137 @@
/*
Copyright 2017 Alex Ong<the.onga@gmail.com>
Copyright 2020 Andrei Purdea<andrei@purdea.ro>
Copyright 2021 Simon Arlott
Copyright 2024 @ keychron (https://www.keychron.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 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 "debounce.h"
#include "timer.h"
#include <stdlib.h>
#ifdef PROTOCOL_CHIBIOS
# if CH_CFG_USE_MEMCORE == FALSE
# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
# endif
#endif
#define ROW_SHIFTER ((matrix_row_t)1)
typedef uint8_t debounce_counter_t;
extern uint8_t debounce_time;
static debounce_counter_t *debounce_counters = NULL;
static fast_timer_t last_time;
static bool counters_need_update;
static bool cooked_changed;
# define DEBOUNCE_ELAPSED 0
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 sym_defer_pk_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 sym_defer_pk_debounce_free(void) {
if (debounce_counters != NULL) {
free(debounce_counters);
debounce_counters = NULL;
}
}
bool sym_defer_pk_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
bool updated_last = false;
cooked_changed = false;
if (counters_need_update) {
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) {
if (!updated_last) {
last_time = timer_read_fast();
}
start_debounce_counters(raw, cooked, num_rows);
}
return cooked_changed;
}
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 (*debounce_pointer <= elapsed_time) {
*debounce_pointer = DEBOUNCE_ELAPSED;
matrix_row_t cooked_next = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col));
cooked_changed |= cooked[row] ^ cooked_next;
cooked[row] = cooked_next;
} else {
*debounce_pointer -= elapsed_time;
counters_need_update = true;
}
}
debounce_pointer++;
}
}
}
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 = debounce_time;;
counters_need_update = true;
}
} else {
*debounce_pointer = DEBOUNCE_ELAPSED;
}
debounce_pointer++;
}
}
}
@@ -0,0 +1,80 @@
/*
Copyright 2021 Chad Austin <chad@chadaustin.me>
Copyright 2024 @ keychron (https://www.keychron.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/>.
*/
/*
Symmetric per-row debounce algorithm. Changes only apply when
DEBOUNCE milliseconds have elapsed since the last change.
*/
#include "debounce.h"
#include "timer.h"
#include <stdlib.h>
extern uint8_t debounce_time;
static uint16_t last_time;
// [row] milliseconds until key's state is considered debounced.
static uint8_t* countdowns = NULL;
// [row]
static matrix_row_t* last_raw = NULL;
void sym_defer_pr_debounce_init(uint8_t num_rows) {
countdowns = (uint8_t*)calloc(num_rows, sizeof(uint8_t));
last_raw = (matrix_row_t*)calloc(num_rows, sizeof(matrix_row_t));
last_time = timer_read();
}
void sym_defer_pr_debounce_free(void) {
if (countdowns != NULL) {
free(countdowns);
countdowns = NULL;
}
if (last_raw != NULL) {
free(last_raw);
last_raw = NULL;
}
}
bool sym_defer_pr_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
uint16_t now = timer_read();
uint16_t elapsed16 = TIMER_DIFF_16(now, last_time);
last_time = now;
uint8_t elapsed = (elapsed16 > 255) ? 255 : elapsed16;
bool cooked_changed = false;
uint8_t* countdown = countdowns;
for (uint8_t row = 0; row < num_rows; ++row, ++countdown) {
matrix_row_t raw_row = raw[row];
if (raw_row != last_raw[row]) {
*countdown = debounce_time;
last_raw[row] = raw_row;
} else if (*countdown > elapsed) {
*countdown -= elapsed;
} else if (*countdown) {
cooked_changed |= cooked[row] ^ raw_row;
cooked[row] = raw_row;
*countdown = 0;
}
}
return cooked_changed;
}
bool debounce_active(void) {
return true;
}
@@ -0,0 +1,142 @@
/*
Copyright 2017 Alex Ong<the.onga@gmail.com>
Copyright 2021 Simon Arlott
Copyright 2024 @ keychron (https://www.keychron.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 "timer.h"
#include <stdlib.h>
#ifdef PROTOCOL_CHIBIOS
# if CH_CFG_USE_MEMCORE == FALSE
# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
# endif
#endif
extern uint8_t debounce_time;
#define ROW_SHIFTER ((matrix_row_t)1)
typedef uint8_t debounce_counter_t;
static debounce_counter_t *debounce_counters = NULL;
static fast_timer_t last_time;
static bool counters_need_update;
static bool matrix_need_update;
static bool cooked_changed;
# define DEBOUNCE_ELAPSED 0
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 sym_eager_pk_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 sym_eager_pk_debounce_free(void) {
if (debounce_counters != NULL) {
free(debounce_counters);
debounce_counters = NULL;
}
}
bool sym_eager_pk_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
bool updated_last = false;
cooked_changed = false;
if (counters_need_update) {
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) {
if (!updated_last) {
last_time = timer_read_fast();
}
transfer_matrix_values(raw, cooked, num_rows);
}
return cooked_changed;
}
// If the current time is > debounce counter, set the counter to enable input.
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 (*debounce_pointer <= elapsed_time) {
*debounce_pointer = DEBOUNCE_ELAPSED;
matrix_need_update = true;
} else {
*debounce_pointer -= elapsed_time;
counters_need_update = true;
}
}
debounce_pointer++;
}
}
}
// upload from raw_matrix to final matrix;
static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
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 = debounce_time;
counters_need_update = true;
existing_row ^= col_mask; // flip the bit.
cooked_changed = true;
}
}
debounce_pointer++;
}
cooked[row] = existing_row;
}
}
@@ -0,0 +1,134 @@
/*
Copyright 2019 Alex Ong<the.onga@gmail.com>
Copyright 2021 Simon Arlott
Copyright 2024 @ keychron (https://www.keychron.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 "debounce.h"
#include "timer.h"
#include <stdlib.h>
#ifdef PROTOCOL_CHIBIOS
# if CH_CFG_USE_MEMCORE == FALSE
# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
# endif
#endif
typedef uint8_t debounce_counter_t;
extern uint8_t debounce_time;
static bool matrix_need_update;
static debounce_counter_t *debounce_counters = NULL;
static fast_timer_t last_time;
static bool counters_need_update;
static bool cooked_changed;
# define DEBOUNCE_ELAPSED 0
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 sym_eager_pr_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 sym_eager_pr_debounce_free(void) {
if (debounce_counters != NULL) {
free(debounce_counters);
debounce_counters = NULL;
}
}
bool sym_eager_pr_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
bool updated_last = false;
cooked_changed = false;
if (counters_need_update) {
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) {
if (!updated_last) {
last_time = timer_read_fast();
}
transfer_matrix_values(raw, cooked, num_rows);
}
return cooked_changed;
}
// If the current time is > debounce counter, set the counter to enable input.
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++) {
if (*debounce_pointer != DEBOUNCE_ELAPSED) {
if (*debounce_pointer <= elapsed_time) {
*debounce_pointer = DEBOUNCE_ELAPSED;
matrix_need_update = true;
} else {
*debounce_pointer -= elapsed_time;
counters_need_update = true;
}
}
debounce_pointer++;
}
}
// upload from raw_matrix to final matrix;
static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
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 = debounce_time;
cooked_changed |= cooked[row] ^ raw_row;
cooked[row] = raw_row;
counters_need_update = true;
}
}
debounce_pointer++;
}
}