From 104697ec40ed82ec1f2ea6e8e8b65c134735ad73 Mon Sep 17 00:00:00 2001 From: Dean Scarff Date: Sun, 14 Feb 2021 03:31:03 +1100 Subject: [PATCH] Use eager reporting when debouncing If a key has been in a stable state and then changes, report that change immediately and then wait for a cooldown period before reporting any additional changes. This reduces latency for registering initial keypresses. It would be unusual for this to introduce chatter, since mechanical switches only tend to be noisy immediately after a key press or release. This change does not introduce any additional persistent state, although it does add an additional parameter to the debouncer to distinguish between the last raw state and the last debounced state. It also fixes a bug whereby all rows in the matrix would be rewritten for every row that changed due to a mis-nesting of loops. However, it now writes to the matrix state unconditionally. Signed-off-by: Dean Scarff --- src/kaleidoscope/driver/keyscanner/ATmega.h | 70 ++++++++++++--------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/src/kaleidoscope/driver/keyscanner/ATmega.h b/src/kaleidoscope/driver/keyscanner/ATmega.h index 3fa4bb83..9afe5314 100644 --- a/src/kaleidoscope/driver/keyscanner/ATmega.h +++ b/src/kaleidoscope/driver/keyscanner/ATmega.h @@ -105,15 +105,11 @@ class ATmega: public kaleidoscope::driver::keyscanner::Base<_KeyScannerProps> { OUTPUT_TOGGLE(_KeyScannerProps::matrix_row_pins[current_row]); - any_debounced_changes |= debounce(hot_pins, &matrix_state_[current_row].debouncer); - - if (any_debounced_changes) { - for (uint8_t current_row = 0; current_row < _KeyScannerProps::matrix_rows; current_row++) { - matrix_state_[current_row].current = matrix_state_[current_row].debouncer.debounced_state; - } - } + matrix_state_[current_row].current ^= + debounce(hot_pins, matrix_state_[current_row].current, &matrix_state_[current_row].debouncer); } } + void scanMatrix() { if (do_scan_) { do_scan_ = false; @@ -164,15 +160,14 @@ class ATmega: public kaleidoscope::driver::keyscanner::Base<_KeyScannerProps> { protected: /* - each of these variables are storing the state for a row of keys - - so for key 0, the counter is represented by db0[0] and db1[0] - and the state in debounced_state[0]. + Each of these variables is storing the state for a row of keys. + So for key 0, the last scan state is in raw[0], and the number of + consecutive scans with key 0 in that same state is (n1[0] << 1) | n0[0]. */ struct debounce_t { - typename _KeyScannerProps::RowState db0; // counter bit 0 - typename _KeyScannerProps::RowState db1; // counter bit 1 - typename _KeyScannerProps::RowState debounced_state; // debounced state + typename _KeyScannerProps::RowState n0; // consecutive scans % 1 + typename _KeyScannerProps::RowState n1; // consecutive scans % 2 + typename _KeyScannerProps::RowState raw; // last key state }; struct row_state_t { @@ -209,29 +204,44 @@ class ATmega: public kaleidoscope::driver::keyscanner::Base<_KeyScannerProps> { return hot_pins; } + /* + * Returns a row with bits set wherever a key's debounced state has changed. + * + * Changes are reported eagerly: if the state is stable and a new change is + * detected, immediately report that change. Whenever the raw state changes, + * the counter is reset. + */ static inline typename _KeyScannerProps::RowState debounce( - typename _KeyScannerProps::RowState sample, debounce_t *debouncer + typename _KeyScannerProps::RowState sample, + typename _KeyScannerProps::RowState current, + debounce_t *db ) { - typename _KeyScannerProps::RowState delta, changes; + // If the counter for a key is non-zero, it is still in a cooldown phase. + typename _KeyScannerProps::RowState unstable = db->n0 | db->n1; + + // Use xor to detect changes from last reported state: + // if a key has changed, its bit will be 1, otherwise 0 + typename _KeyScannerProps::RowState delta = sample ^ current; - // Use xor to detect changes from last stable state: - // if a key has changed, it's bit will be 1, otherwise 0 - delta = sample ^ debouncer->debounced_state; + // Ignore any keys in their cooldown phase. + delta &= ~unstable; - // Increment counters and reset any unchanged bits: - // increment bit 1 for all changed keys - debouncer->db1 = ((debouncer->db1) ^ (debouncer->db0)) & delta; - // increment bit 0 for all changed keys - debouncer->db0 = ~(debouncer->db0) & delta; + // Reset cooldown counter on any changed (raw) keys to 0. + typename _KeyScannerProps::RowState changed = sample ^ db->raw; + unstable |= changed; + db->n1 &= ~changed; + db->n0 &= ~changed; - // Calculate returned change set: if delta is still true - // and the counter has wrapped back to 0, the key is changed. + // Increment cooldown counters for keys that are unstable: + // n1 = (n + 1) % 2 + db->n1 = (db->n1 ^ db->n0) & unstable; + // n0 = (n + 1) % 1 + db->n0 = ~db->n0 & unstable; - changes = ~(~delta | (debouncer->db0) | (debouncer->db1)); - // Update state: in this case use xor to flip any bit that is true in changes. - debouncer->debounced_state ^= changes; + // Save the raw sample for the next scan. + db->raw = sample; - return changes; + return delta; } }; #else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD