asym_eager_defer_pk.c (5998B)
1 // Copyright 2017 Alex Ong <the.onga@gmail.com> 2 // Copyright 2020 Andrei Purdea <andrei@purdea.ro> 3 // Copyright 2021 Simon Arlott 4 // SPDX-License-Identifier: GPL-2.0-or-later 5 // 6 // Asymetric per-key algorithm. After pressing a key, it immediately changes state, 7 // with no further inputs accepted until DEBOUNCE milliseconds have occurred. After 8 // releasing a key, that state is pushed after no changes occur for DEBOUNCE milliseconds. 9 10 #include "debounce.h" 11 #include "timer.h" 12 #include "util.h" 13 14 #ifndef DEBOUNCE 15 # define DEBOUNCE 5 16 #endif 17 18 // Maximum debounce: 127ms 19 #if DEBOUNCE > 127 20 # undef DEBOUNCE 21 # define DEBOUNCE 127 22 #endif 23 24 #define DEBOUNCE_ELAPSED 0 25 26 #if DEBOUNCE > 0 27 typedef struct { 28 bool pressed : 1; 29 uint8_t time : 7; 30 } debounce_counter_t; 31 32 // Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards 33 static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED}; 34 static bool counters_need_update; 35 static bool matrix_need_update; 36 static bool cooked_changed; 37 38 static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time); 39 static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]); 40 41 void debounce_init(void) {} 42 43 bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) { 44 static fast_timer_t last_time; 45 bool updated_last = false; 46 cooked_changed = false; 47 48 if (counters_need_update) { 49 fast_timer_t now = timer_read_fast(); 50 fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); 51 52 last_time = now; 53 updated_last = true; 54 55 if (elapsed_time > 0) { 56 // Update debounce counters with elapsed timer clamped to 127 (maximum debounce) 57 update_debounce_counters_and_transfer_if_expired(raw, cooked, MIN(elapsed_time, 127)); 58 } 59 } 60 61 if (changed || matrix_need_update) { 62 if (!updated_last) { 63 last_time = timer_read_fast(); 64 } 65 66 transfer_matrix_values(raw, cooked); 67 } 68 69 return cooked_changed; 70 } 71 72 /** 73 * @brief Processes per-key debounce counters and updates the debounced matrix state. 74 * 75 * This function iterates through each key in the matrix and updates its debounce counter 76 * based on the elapsed time. If the debounce period has expired, the debounced state is 77 * updated accordingly for key-down (eager) and key-up (defer) events. 78 * 79 * @param raw The current raw key state matrix. 80 * @param cooked The debounced key state matrix to be updated. 81 * @param elapsed_time The time elapsed since the last debounce update, in milliseconds. 82 */ 83 static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time) { 84 counters_need_update = false; 85 matrix_need_update = false; 86 87 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { 88 uint16_t row_offset = row * MATRIX_COLS; 89 90 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 91 uint16_t index = row_offset + col; 92 93 if (debounce_counters[index].time != DEBOUNCE_ELAPSED) { 94 if (debounce_counters[index].time <= elapsed_time) { 95 debounce_counters[index].time = DEBOUNCE_ELAPSED; 96 97 if (debounce_counters[index].pressed) { 98 // key-down: eager 99 matrix_need_update = true; 100 } else { 101 // key-up: defer 102 matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col); 103 matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask); 104 cooked_changed |= cooked_next ^ cooked[row]; 105 cooked[row] = cooked_next; 106 } 107 } else { 108 debounce_counters[index].time -= elapsed_time; 109 counters_need_update = true; 110 } 111 } 112 } 113 } 114 } 115 116 /** 117 * @brief Applies debounced changes to the matrix state based on per-key counters. 118 * 119 * This function compares the raw and cooked key state matrices to detect changes. 120 * For each key, it updates the debounce counter and the debounced state according 121 * to the debounce algorithm. Key-down events are handled eagerly, while key-up 122 * events are deferred until the debounce period has elapsed. 123 * 124 * @param raw The current raw key state matrix. 125 * @param cooked The debounced key state matrix to be updated. 126 */ 127 static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]) { 128 matrix_need_update = false; 129 130 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) { 131 uint16_t row_offset = row * MATRIX_COLS; 132 matrix_row_t delta = raw[row] ^ cooked[row]; 133 134 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 135 uint16_t index = row_offset + col; 136 matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col); 137 138 if (delta & col_mask) { 139 if (debounce_counters[index].time == DEBOUNCE_ELAPSED) { 140 debounce_counters[index].pressed = (raw[row] & col_mask); 141 debounce_counters[index].time = DEBOUNCE; 142 counters_need_update = true; 143 144 if (debounce_counters[index].pressed) { 145 // key-down: eager 146 cooked[row] ^= col_mask; 147 cooked_changed = true; 148 } 149 } 150 } else if (debounce_counters[index].time != DEBOUNCE_ELAPSED) { 151 if (!debounce_counters[index].pressed) { 152 // key-up: defer 153 debounce_counters[index].time = DEBOUNCE_ELAPSED; 154 } 155 } 156 } 157 } 158 } 159 160 #else 161 # include "none.c" 162 #endif