Debounce refactor / API (#3720)
	
		
	
				
					
				
			* Added xeal60 via clone of lets split * Delete removed other keymaps * Basic keymap (no FN). Compiles. * Removed NP_STAR and NP_SLSH. * Removed "debounce_algo = manual" in all keyboards with CUSTOM_MATRIX = yes. * Changed order of rules in TMK. Documented feature. * Fixed missing whitespace in debounce documentation Table wasn't working due to missing newline. * Added bold in a few areas. * DO NOT USE - Removed debounce from TMK. * Remove accidental xeal60 commit * DO NOT USE - debounce successfully compiled. * DO NOT USE Revert back to original API to support split_keyboards. * Working eager_pk * Whitespace cleanup. * Restored debounce.h since there wasnt any real change. * Moved debouncing_time variable to inside #if debounce * Removed check for custom_matrix. We can safely include the debounce file for compilation when custom_matrix is used. * Removed #include "matrix.h" from debounce.h * Bug fix - was using MATRIX_ROWS instead of num_rows * Fixed compilation error with debounce_sym_g * Renamed DEBOUNCE_ALGO to DEBOUNCE_TYPE * Malloc array in debounce_eager_pk, since split keyboards only use MATRIX_ROWS/2. * Fix compile error in debounce_eager_pk * Stricter, leaner DEBOUNCE_TYPE section in common_features.mk. Cleanup debounce_type.mkpull/5134/head 0.6.280
						commit
						c22f3ba3a2
					
				@ -0,0 +1,46 @@
 | 
				
			||||
# Debounce algorithm
 | 
				
			||||
 | 
				
			||||
QMK supports multiple debounce algorithms through its debounce API.
 | 
				
			||||
 | 
				
			||||
The underlying debounce algorithm is determined by which matrix.c file you are using.
 | 
				
			||||
 | 
				
			||||
The logic for which debounce method called is below. It checks various defines that you have set in rules.mk
 | 
				
			||||
 | 
				
			||||
```
 | 
				
			||||
DEBOUNCE_TYPE?= sym_g
 | 
				
			||||
VALID_DEBOUNCE_TYPES := sym_g eager_pk custom
 | 
				
			||||
ifeq ($(filter $(DEBOUNCE_TYPE),$(VALID_DEBOUNCE_TYPES)),)
 | 
				
			||||
    $(error DEBOUNCE_TYPE="$(DEBOUNCE_TYPE)" is not a valid debounce algorithm)
 | 
				
			||||
endif
 | 
				
			||||
ifeq ($(strip $(DEBOUNCE_TYPE)), sym_g)
 | 
				
			||||
    QUANTUM_SRC += $(DEBOUNCE_DIR)/debounce_sym_g.c
 | 
				
			||||
else ifeq ($(strip $(DEBOUNCE_TYPE)), eager_pk)
 | 
				
			||||
    QUANTUM_SRC += $(DEBOUNCE_DIR)/debounce_eager_pk.c
 | 
				
			||||
endif
 | 
				
			||||
```
 | 
				
			||||
 | 
				
			||||
# Debounce selection
 | 
				
			||||
 | 
				
			||||
| DEBOUNCE_ALGO    | Description                                                 | What to do                    |
 | 
				
			||||
| -------------    | ---------------------------------------------------         | ----------------------------- |
 | 
				
			||||
| Not defined      | You are using the included matrix.c and debounce.c          | Nothing. Debounce_sym_g will be compiled, and used if necessary |
 | 
				
			||||
| custom           | Use your own debounce.c                                     | ```SRC += debounce.c``` add your own debounce.c and implement necessary functions |
 | 
				
			||||
| sym_g / eager_pk | You are using the included matrix.c and debounce.c          | Use an alternative debounce algorithm |
 | 
				
			||||
 | 
				
			||||
**Regarding split keyboards**: 
 | 
				
			||||
The debounce code is compatible with split keyboards.
 | 
				
			||||
 | 
				
			||||
# Use your own debouncing code
 | 
				
			||||
* Set ```DEBOUNCE_TYPE = custom ```.
 | 
				
			||||
* Add ```SRC += debounce.c```
 | 
				
			||||
* Add your own ```debounce.c```. Look at included ```debounce_sym_g.c```s for sample implementations.
 | 
				
			||||
* Debouncing occurs after every raw matrix scan.
 | 
				
			||||
* Use num_rows rather than MATRIX_ROWS, so that split keyboards are supported correctly.
 | 
				
			||||
 | 
				
			||||
# Changing between included debouncing methods
 | 
				
			||||
You can either use your own code, by including your own debounce.c, or switch to another included one.
 | 
				
			||||
Included debounce methods are:
 | 
				
			||||
* debounce_eager_pk - debouncing per key. On any state change, response is immediate, followed by ```DEBOUNCE_DELAY``` millseconds of no further input for that key
 | 
				
			||||
* debounce_sym_g - debouncing per keyboard. On any state change, a global timer is set. When ```DEBOUNCE_DELAY``` milliseconds of no changes has occured, all input changes are pushed.
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
@ -1,52 +0,0 @@
 | 
				
			||||
 | 
				
			||||
#include "matrix.h"
 | 
				
			||||
#include "timer.h"
 | 
				
			||||
#include "quantum.h"
 | 
				
			||||
 | 
				
			||||
#ifndef DEBOUNCING_DELAY
 | 
				
			||||
#  define DEBOUNCING_DELAY 5
 | 
				
			||||
#endif
 | 
				
			||||
 | 
				
			||||
void debounce_init(uint8_t num_rows) {
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
#if DEBOUNCING_DELAY > 0
 | 
				
			||||
 | 
				
			||||
static bool debouncing = false;
 | 
				
			||||
 | 
				
			||||
void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
 | 
				
			||||
  static uint16_t debouncing_time;
 | 
				
			||||
 | 
				
			||||
  if (changed) {
 | 
				
			||||
    debouncing = true;
 | 
				
			||||
    debouncing_time = timer_read();
 | 
				
			||||
  }
 | 
				
			||||
 | 
				
			||||
  if (debouncing && (timer_elapsed(debouncing_time) > DEBOUNCING_DELAY)) {
 | 
				
			||||
    for (uint8_t i = 0; i < num_rows; i++) {
 | 
				
			||||
      cooked[i] = raw[i];
 | 
				
			||||
    }
 | 
				
			||||
    debouncing = false;
 | 
				
			||||
  }
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
bool debounce_active(void) {
 | 
				
			||||
  return debouncing;
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
#else
 | 
				
			||||
 | 
				
			||||
// no debounce
 | 
				
			||||
void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
 | 
				
			||||
  if (changed)
 | 
				
			||||
  {
 | 
				
			||||
  for (uint8_t i = 0; i < num_rows; i++) {
 | 
				
			||||
      cooked[i] = raw[i];
 | 
				
			||||
    }
 | 
				
			||||
  }
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
bool debounce_active(void) {
 | 
				
			||||
  return false;
 | 
				
			||||
}
 | 
				
			||||
#endif
 | 
				
			||||
@ -0,0 +1,121 @@
 | 
				
			||||
/*
 | 
				
			||||
Copyright 2017 Alex Ong<the.onga@gmail.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 "matrix.h"
 | 
				
			||||
#include "timer.h"
 | 
				
			||||
#include "quantum.h"
 | 
				
			||||
#include <stdlib.h>
 | 
				
			||||
 | 
				
			||||
#ifndef DEBOUNCE
 | 
				
			||||
  #define DEBOUNCE 5
 | 
				
			||||
#endif
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
#if (MATRIX_COLS <= 8)
 | 
				
			||||
#    define ROW_SHIFTER ((uint8_t)1)
 | 
				
			||||
#elif (MATRIX_COLS <= 16)
 | 
				
			||||
#    define ROW_SHIFTER ((uint16_t)1)
 | 
				
			||||
#elif (MATRIX_COLS <= 32)
 | 
				
			||||
#    define ROW_SHIFTER  ((uint32_t)1)
 | 
				
			||||
#endif
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
#define debounce_counter_t uint8_t
 | 
				
			||||
 | 
				
			||||
static debounce_counter_t *debounce_counters;
 | 
				
			||||
 | 
				
			||||
#define DEBOUNCE_ELAPSED 251
 | 
				
			||||
#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1)
 | 
				
			||||
 | 
				
			||||
void update_debounce_counters(uint8_t num_rows, uint8_t current_time);
 | 
				
			||||
void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time);
 | 
				
			||||
 | 
				
			||||
//we use num_rows rather than MATRIX_ROWS to support split keyboards
 | 
				
			||||
void 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 debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed)
 | 
				
			||||
{
 | 
				
			||||
  uint8_t current_time = timer_read() % MAX_DEBOUNCE;
 | 
				
			||||
  update_debounce_counters(num_rows, current_time);
 | 
				
			||||
  transfer_matrix_values(raw, cooked, num_rows, current_time);
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
//If the current time is > debounce counter, set the counter to enable input.
 | 
				
			||||
void update_debounce_counters(uint8_t num_rows, uint8_t current_time)
 | 
				
			||||
{
 | 
				
			||||
  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 (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) {
 | 
				
			||||
          *debounce_pointer = DEBOUNCE_ELAPSED;
 | 
				
			||||
        }
 | 
				
			||||
      }
 | 
				
			||||
      debounce_pointer++;
 | 
				
			||||
    }
 | 
				
			||||
  }
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
// upload from raw_matrix to final matrix;
 | 
				
			||||
void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time)
 | 
				
			||||
{
 | 
				
			||||
  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];
 | 
				
			||||
 | 
				
			||||
    for (uint8_t col = 0; col < MATRIX_COLS; col++)
 | 
				
			||||
    {
 | 
				
			||||
      matrix_row_t col_mask = (ROW_SHIFTER << col);
 | 
				
			||||
      bool final_value = raw_row & col_mask;
 | 
				
			||||
      bool existing_value = existing_row & col_mask;
 | 
				
			||||
      if (*debounce_pointer == DEBOUNCE_ELAPSED &&
 | 
				
			||||
          (existing_value != final_value))
 | 
				
			||||
      {
 | 
				
			||||
        *debounce_pointer = current_time;
 | 
				
			||||
        existing_row ^= col_mask; //flip the bit.
 | 
				
			||||
      }
 | 
				
			||||
      debounce_pointer++;
 | 
				
			||||
    }
 | 
				
			||||
    cooked[row] = existing_row;
 | 
				
			||||
  }  
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
bool debounce_active(void)
 | 
				
			||||
{
 | 
				
			||||
  return true;
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
@ -0,0 +1,57 @@
 | 
				
			||||
/*
 | 
				
			||||
Copyright 2017 Alex Ong<the.onga@gmail.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 "matrix.h"
 | 
				
			||||
#include "timer.h"
 | 
				
			||||
#include "quantum.h"
 | 
				
			||||
#ifndef DEBOUNCE
 | 
				
			||||
  #define DEBOUNCE 5
 | 
				
			||||
#endif
 | 
				
			||||
 | 
				
			||||
void debounce_init(uint8_t num_rows) {}
 | 
				
			||||
static bool debouncing = false;
 | 
				
			||||
 | 
				
			||||
#if DEBOUNCE > 0
 | 
				
			||||
static uint16_t debouncing_time;
 | 
				
			||||
void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed)
 | 
				
			||||
{
 | 
				
			||||
  if (changed) {
 | 
				
			||||
    debouncing = true;
 | 
				
			||||
    debouncing_time = timer_read();
 | 
				
			||||
  }
 | 
				
			||||
 | 
				
			||||
  if (debouncing && timer_elapsed(debouncing_time) > DEBOUNCE) {
 | 
				
			||||
    for (int i = 0; i < num_rows; i++) {
 | 
				
			||||
      cooked[i] = raw[i];
 | 
				
			||||
    }
 | 
				
			||||
    debouncing = false;
 | 
				
			||||
  }
 | 
				
			||||
}
 | 
				
			||||
#else //no debouncing.
 | 
				
			||||
void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed)
 | 
				
			||||
{
 | 
				
			||||
  for (int i = 0; i < num_rows; i++) {
 | 
				
			||||
    cooked[i] = raw[i];
 | 
				
			||||
  }
 | 
				
			||||
}
 | 
				
			||||
#endif
 | 
				
			||||
 | 
				
			||||
bool debounce_active(void) {
 | 
				
			||||
  return debouncing;
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
@ -0,0 +1,28 @@
 | 
				
			||||
Debounce algorithms belong in this folder.
 | 
				
			||||
Here are a few ideas
 | 
				
			||||
 | 
				
			||||
1) Global vs Per-Key vs Per-Row
 | 
				
			||||
 * Global - one timer for all keys. Any key change state affects global timer
 | 
				
			||||
 * Per key - one timer per key
 | 
				
			||||
 * Per row - one timer per row
 | 
				
			||||
 | 
				
			||||
2) Eager vs symmetric vs assymetric
 | 
				
			||||
 * Eager - any key change is reported immediately. All further inputs for DEBOUNCE ms are ignored.
 | 
				
			||||
 * Symmetric - wait for no changes for DEBOUNCE ms before reporting change
 | 
				
			||||
 * Assymetric - wait for different times depending on key-down/key-up. E.g. Eager key-down, DEBOUNCE ms key up.
 | 
				
			||||
 | 
				
			||||
3) Timestamp vs cycles
 | 
				
			||||
 * old old old code waits n cycles, decreasing count by one each matrix_scan
 | 
				
			||||
 * newer code stores the millisecond the change occurred, and does subraction to figure out time elapsed.
 | 
				
			||||
 * Timestamps are superior, i don't think cycles will ever be used again once upgraded.
 | 
				
			||||
 | 
				
			||||
The default algorithm is symmetric and global.
 | 
				
			||||
Here are a few that could be implemented:
 | 
				
			||||
 | 
				
			||||
debounce_sym_g.c
 | 
				
			||||
debounce_sym_pk.c 
 | 
				
			||||
debounce_sym_pr.c 
 | 
				
			||||
debounce_sym_pr_cycles.c //currently used in ergo-dox
 | 
				
			||||
debounce_eager_g.c
 | 
				
			||||
debounce_eager_pk.c
 | 
				
			||||
debounce_eager_pr.c //could be used in ergo-dox!
 | 
				
			||||
					Loading…
					
					
				
		Reference in new issue