forked from zmkfirmware/zmk
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(kscan): Improve matrix debouncing
Switched the GPIO matrix driver to debouncing using a simple integrator algorithm. Whenever a key is pressed, we now scan at a rate controlled by debounce-scan-period-ms (default 1 ms) until all keys are released, then return to either waiting for an interrupt or polling more slowly. The timers for key press and release can now be controlled separately, so debounce-period is deprecated in favor of debounce-press-ms and debounce-release-ms. Global Kconfig options ZMK_KSCAN_DEBOUNCE_PRESS_MS and ZMK_KSCAN_DEBOUNCE_RELEASE_MS are also added to make these easier to set. Added documentation for debouncing options.
- Loading branch information
1 parent
5cc7c28
commit f946dc6
Showing
8 changed files
with
348 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* | ||
* Copyright (c) 2021 The ZMK Contributors | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
#include "debounce.h" | ||
|
||
static uint32_t get_threshold(const struct debounce_state *state, | ||
const struct debounce_config *config) { | ||
return state->pressed ? config->debounce_release_ms : config->debounce_press_ms; | ||
} | ||
|
||
static void increment_counter(struct debounce_state *state, const int elapsed_ms) { | ||
if (state->counter + elapsed_ms > DEBOUNCE_COUNTER_MAX) { | ||
state->counter = DEBOUNCE_COUNTER_MAX; | ||
} else { | ||
state->counter += elapsed_ms; | ||
} | ||
} | ||
|
||
static void decrement_counter(struct debounce_state *state, const int elapsed_ms) { | ||
if (state->counter < elapsed_ms) { | ||
state->counter = 0; | ||
} else { | ||
state->counter -= elapsed_ms; | ||
} | ||
} | ||
|
||
void debounce_update(struct debounce_state *state, const bool active, const int elapsed_ms, | ||
const struct debounce_config *config) { | ||
// This uses a variation of the integrator debouncing described at | ||
// https://www.kennethkuhn.com/electronics/debounce.c | ||
// Every update where "active" does not match the current state, we increment | ||
// a counter, otherwise we decrement it. When the counter reaches a | ||
// threshold, the state flips and we reset the counter. | ||
state->changed = false; | ||
|
||
if (active == state->pressed) { | ||
decrement_counter(state, elapsed_ms); | ||
return; | ||
} | ||
|
||
const uint32_t flip_threshold = get_threshold(state, config); | ||
|
||
if (state->counter < flip_threshold) { | ||
increment_counter(state, elapsed_ms); | ||
return; | ||
} | ||
|
||
state->pressed = !state->pressed; | ||
state->counter = 0; | ||
state->changed = true; | ||
} | ||
|
||
bool debounce_is_active(const struct debounce_state *state) { | ||
return state->pressed || state->counter > 0; | ||
} | ||
|
||
bool debounce_is_pressed(const struct debounce_state *state) { return state->pressed; } | ||
|
||
bool debounce_get_changed(const struct debounce_state *state) { return state->changed; } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* Copyright (c) 2021 The ZMK Contributors | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <stdbool.h> | ||
#include <stdint.h> | ||
#include <sys/util.h> | ||
|
||
#define DEBOUNCE_COUNTER_BITS 14 | ||
#define DEBOUNCE_COUNTER_MAX BIT_MASK(DEBOUNCE_COUNTER_BITS) | ||
|
||
struct debounce_state { | ||
bool pressed : 1; | ||
bool changed : 1; | ||
uint16_t counter : DEBOUNCE_COUNTER_BITS; | ||
}; | ||
|
||
struct debounce_config { | ||
/** Duration a switch must be pressed to latch as pressed. */ | ||
uint32_t debounce_press_ms; | ||
/** Duration a switch must be released to latch as released. */ | ||
uint32_t debounce_release_ms; | ||
}; | ||
|
||
/** | ||
* Debounces one switch. | ||
* | ||
* @param state The state for the switch to debounce. | ||
* @param active Is the switch currently pressed? | ||
* @param elapsed_ms Time elapsed since the previous update in milliseconds. | ||
* @param config Debounce settings. | ||
*/ | ||
void debounce_update(struct debounce_state *state, const bool active, const int elapsed_ms, | ||
const struct debounce_config *config); | ||
|
||
/** | ||
* @returns whether the switch is either latched as pressed or it is potentially | ||
* pressed but the debouncer has not yet made a decision. If this returns true, | ||
* the kscan driver should continue to poll quickly. | ||
*/ | ||
bool debounce_is_active(const struct debounce_state *state); | ||
|
||
/** | ||
* @returns whether the switch is latched as pressed. | ||
*/ | ||
bool debounce_is_pressed(const struct debounce_state *state); | ||
|
||
/** | ||
* @returns whether the pressed state of the switch changed in the last call to | ||
* debounce_update. | ||
*/ | ||
bool debounce_get_changed(const struct debounce_state *state); |
Oops, something went wrong.