asym_eager_defer_pk.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. #include "debounce.h"
  10. #include "timer.h"
  11. #include "util.h"
  12. #ifndef DEBOUNCE
  13. # define DEBOUNCE 5
  14. #endif
  15. // Maximum debounce: 127ms
  16. #if DEBOUNCE > 127
  17. # undef DEBOUNCE
  18. # define DEBOUNCE 127
  19. #endif
  20. #define DEBOUNCE_ELAPSED 0
  21. #if DEBOUNCE > 0
  22. typedef struct {
  23. bool pressed : 1;
  24. uint8_t time : 7;
  25. } debounce_counter_t;
  26. // Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards
  27. static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED};
  28. static bool counters_need_update;
  29. static bool matrix_need_update;
  30. static bool cooked_changed;
  31. static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time);
  32. static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]);
  33. void debounce_init(void) {}
  34. bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
  35. static fast_timer_t last_time;
  36. bool updated_last = false;
  37. cooked_changed = false;
  38. if (counters_need_update) {
  39. fast_timer_t now = timer_read_fast();
  40. fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
  41. last_time = now;
  42. updated_last = true;
  43. if (elapsed_time > 0) {
  44. // Update debounce counters with elapsed timer clamped to 127 (maximum debounce)
  45. update_debounce_counters_and_transfer_if_expired(raw, cooked, MIN(elapsed_time, 127));
  46. }
  47. }
  48. if (changed || matrix_need_update) {
  49. if (!updated_last) {
  50. last_time = timer_read_fast();
  51. }
  52. transfer_matrix_values(raw, cooked);
  53. }
  54. return cooked_changed;
  55. }
  56. /**
  57. * @brief Processes per-key debounce counters and updates the debounced matrix state.
  58. *
  59. * This function iterates through each key in the matrix and updates its debounce counter
  60. * based on the elapsed time. If the debounce period has expired, the debounced state is
  61. * updated accordingly for key-down (eager) and key-up (defer) events.
  62. *
  63. * @param raw The current raw key state matrix.
  64. * @param cooked The debounced key state matrix to be updated.
  65. * @param elapsed_time The time elapsed since the last debounce update, in milliseconds.
  66. */
  67. static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time) {
  68. counters_need_update = false;
  69. matrix_need_update = false;
  70. for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
  71. uint16_t row_offset = row * MATRIX_COLS;
  72. for (uint8_t col = 0; col < MATRIX_COLS; col++) {
  73. uint16_t index = row_offset + col;
  74. if (debounce_counters[index].time != DEBOUNCE_ELAPSED) {
  75. if (debounce_counters[index].time <= elapsed_time) {
  76. debounce_counters[index].time = DEBOUNCE_ELAPSED;
  77. if (debounce_counters[index].pressed) {
  78. // key-down: eager
  79. matrix_need_update = true;
  80. } else {
  81. // key-up: defer
  82. matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col);
  83. matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask);
  84. cooked_changed |= cooked_next ^ cooked[row];
  85. cooked[row] = cooked_next;
  86. }
  87. } else {
  88. debounce_counters[index].time -= elapsed_time;
  89. counters_need_update = true;
  90. }
  91. }
  92. }
  93. }
  94. }
  95. /**
  96. * @brief Applies debounced changes to the matrix state based on per-key counters.
  97. *
  98. * This function compares the raw and cooked key state matrices to detect changes.
  99. * For each key, it updates the debounce counter and the debounced state according
  100. * to the debounce algorithm. Key-down events are handled eagerly, while key-up
  101. * events are deferred until the debounce period has elapsed.
  102. *
  103. * @param raw The current raw key state matrix.
  104. * @param cooked The debounced key state matrix to be updated.
  105. */
  106. static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]) {
  107. matrix_need_update = false;
  108. for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
  109. uint16_t row_offset = row * MATRIX_COLS;
  110. matrix_row_t delta = raw[row] ^ cooked[row];
  111. for (uint8_t col = 0; col < MATRIX_COLS; col++) {
  112. uint16_t index = row_offset + col;
  113. matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col);
  114. if (delta & col_mask) {
  115. if (debounce_counters[index].time == DEBOUNCE_ELAPSED) {
  116. debounce_counters[index].pressed = (raw[row] & col_mask);
  117. debounce_counters[index].time = DEBOUNCE;
  118. counters_need_update = true;
  119. if (debounce_counters[index].pressed) {
  120. // key-down: eager
  121. cooked[row] ^= col_mask;
  122. cooked_changed = true;
  123. }
  124. }
  125. } else if (debounce_counters[index].time != DEBOUNCE_ELAPSED) {
  126. if (!debounce_counters[index].pressed) {
  127. // key-up: defer
  128. debounce_counters[index].time = DEBOUNCE_ELAPSED;
  129. }
  130. }
  131. }
  132. }
  133. }
  134. #else
  135. # include "none.c"
  136. #endif