diff options
Diffstat (limited to 'quantum/debounce')
| -rw-r--r-- | quantum/debounce/asym_eager_defer_pk.c | 171 | ||||
| -rw-r--r-- | quantum/debounce/none.c | 31 | ||||
| -rw-r--r-- | quantum/debounce/sym_defer_g.c | 26 | ||||
| -rw-r--r-- | quantum/debounce/sym_defer_pk.c | 67 | ||||
| -rw-r--r-- | quantum/debounce/sym_eager_pk.c | 72 | ||||
| -rw-r--r-- | quantum/debounce/sym_eager_pr.c | 76 | ||||
| -rw-r--r-- | quantum/debounce/tests/asym_eager_defer_pk_tests.cpp | 374 | ||||
| -rw-r--r-- | quantum/debounce/tests/debounce_test_common.cpp | 229 | ||||
| -rw-r--r-- | quantum/debounce/tests/debounce_test_common.h | 83 | ||||
| -rw-r--r-- | quantum/debounce/tests/rules.mk | 44 | ||||
| -rw-r--r-- | quantum/debounce/tests/sym_defer_g_tests.cpp | 223 | ||||
| -rw-r--r-- | quantum/debounce/tests/sym_defer_pk_tests.cpp | 225 | ||||
| -rw-r--r-- | quantum/debounce/tests/sym_eager_pk_tests.cpp | 237 | ||||
| -rw-r--r-- | quantum/debounce/tests/sym_eager_pr_tests.cpp | 280 | ||||
| -rw-r--r-- | quantum/debounce/tests/testlist.mk | 6 | 
15 files changed, 2057 insertions, 87 deletions
| diff --git a/quantum/debounce/asym_eager_defer_pk.c b/quantum/debounce/asym_eager_defer_pk.c new file mode 100644 index 0000000000..24380dc5e5 --- /dev/null +++ b/quantum/debounce/asym_eager_defer_pk.c @@ -0,0 +1,171 @@ +/* + * Copyright 2017 Alex Ong <the.onga@gmail.com> + * Copyright 2020 Andrei Purdea <andrei@purdea.ro> + * Copyright 2021 Simon Arlott + * + * 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 symmetric per-key algorithm. Uses an 8-bit counter per key. +When no state changes have occured for DEBOUNCE milliseconds, we push the state. +*/ + +#include "matrix.h" +#include "timer.h" +#include "quantum.h" +#include <stdlib.h> + +#ifdef PROTOCOL_CHIBIOS +#    if CH_CFG_USE_MEMCORE == FALSE +#        error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm. +#    endif +#endif + +#ifndef DEBOUNCE +#    define DEBOUNCE 5 +#endif + +// Maximum debounce: 127ms +#if DEBOUNCE > 127 +#    undef DEBOUNCE +#    define DEBOUNCE 127 +#endif + +#define ROW_SHIFTER ((matrix_row_t)1) + +typedef struct { +    bool pressed : 1; +    uint8_t time : 7; +} debounce_counter_t; + +#if DEBOUNCE > 0 +static debounce_counter_t *debounce_counters; +static fast_timer_t last_time; +static bool counters_need_update; +static bool matrix_need_update; + +#define DEBOUNCE_ELAPSED 0 + +static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time); +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); + +// we use num_rows rather than MATRIX_ROWS to support split keyboards +void debounce_init(uint8_t num_rows) { +    debounce_counters = 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++].time = DEBOUNCE_ELAPSED; +        } +    } +} + +void debounce_free(void) { +    free(debounce_counters); +    debounce_counters = NULL; +} + +void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { +    bool updated_last = false; + +    if (counters_need_update) { +        fast_timer_t now = timer_read_fast(); +        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); + +        last_time = now; +        updated_last = true; +        if (elapsed_time > UINT8_MAX) { +            elapsed_time = UINT8_MAX; +        } + +        if (elapsed_time > 0) { +            update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time); +        } +    } + +    if (changed || matrix_need_update) { +        if (!updated_last) { +            last_time = timer_read_fast(); +        } + +        transfer_matrix_values(raw, cooked, num_rows); +    } +} + +static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) { +    debounce_counter_t *debounce_pointer = debounce_counters; + +    counters_need_update = false; +    matrix_need_update = false; + +    for (uint8_t row = 0; row < num_rows; row++) { +        for (uint8_t col = 0; col < MATRIX_COLS; col++) { +            matrix_row_t col_mask = (ROW_SHIFTER << col); + +            if (debounce_pointer->time != DEBOUNCE_ELAPSED) { +                if (debounce_pointer->time <= elapsed_time) { +                    debounce_pointer->time = DEBOUNCE_ELAPSED; + +                    if (debounce_pointer->pressed) { +                        // key-down: eager +                        matrix_need_update = true; +                    } else { +                        // key-up: defer +                        cooked[row] = (cooked[row] & ~col_mask) | (raw[row] & col_mask); +                    } +                } else { +                    debounce_pointer->time -= elapsed_time; +                    counters_need_update = true; +                } +            } +            debounce_pointer++; +        } +    } +} + +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { +    debounce_counter_t *debounce_pointer = debounce_counters; + +    for (uint8_t row = 0; row < num_rows; row++) { +        matrix_row_t delta = raw[row] ^ cooked[row]; +        for (uint8_t col = 0; col < MATRIX_COLS; col++) { +            matrix_row_t col_mask = (ROW_SHIFTER << col); + +            if (delta & col_mask) { +                if (debounce_pointer->time == DEBOUNCE_ELAPSED) { +                    debounce_pointer->pressed = (raw[row] & col_mask); +                    debounce_pointer->time = DEBOUNCE; +                    counters_need_update = true; + +                    if (debounce_pointer->pressed) { +                        // key-down: eager +                        cooked[row] ^= col_mask; +                    } +                } +            } else if (debounce_pointer->time != DEBOUNCE_ELAPSED) { +                if (!debounce_pointer->pressed) { +                    // key-up: defer +                    debounce_pointer->time = DEBOUNCE_ELAPSED; +                } +            } +            debounce_pointer++; +        } +    } +} + +bool debounce_active(void) { return true; } +#else +#    include "none.c" +#endif diff --git a/quantum/debounce/none.c b/quantum/debounce/none.c new file mode 100644 index 0000000000..b03892bc5b --- /dev/null +++ b/quantum/debounce/none.c @@ -0,0 +1,31 @@ +/* Copyright 2021 Simon Arlott + * + * 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/>. + */ + +#include "matrix.h" +#include "quantum.h" +#include <stdlib.h> + +void debounce_init(uint8_t num_rows) {} + +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]; +    } +} + +bool debounce_active(void) { return false; } + +void debounce_free(void) {} diff --git a/quantum/debounce/sym_defer_g.c b/quantum/debounce/sym_defer_g.c index 3ed9055d2a..fbefd55ede 100644 --- a/quantum/debounce/sym_defer_g.c +++ b/quantum/debounce/sym_defer_g.c @@ -1,5 +1,6 @@  /*  Copyright 2017 Alex Ong<the.onga@gmail.com> +Copyright 2021 Simon Arlott  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 @@ -23,30 +24,29 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.  #    define DEBOUNCE 5  #endif -void        debounce_init(uint8_t num_rows) {} +#if DEBOUNCE > 0  static bool debouncing = false; +static fast_timer_t debouncing_time; -#if DEBOUNCE > 0 -static uint16_t debouncing_time; -void            debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { +void debounce_init(uint8_t num_rows) {} + +void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {      if (changed) {          debouncing      = true; -        debouncing_time = timer_read(); +        debouncing_time = timer_read_fast();      } -    if (debouncing && timer_elapsed(debouncing_time) > DEBOUNCE) { +    if (debouncing && timer_elapsed_fast(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; } + +void debounce_free(void) {} +#else  // no debouncing. +#    include "none.c" +#endif diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c index 60513f98e1..626a9be841 100644 --- a/quantum/debounce/sym_defer_pk.c +++ b/quantum/debounce/sym_defer_pk.c @@ -1,6 +1,7 @@  /*  Copyright 2017 Alex Ong<the.onga@gmail.com>  Copyright 2020 Andrei Purdea<andrei@purdea.ro> +Copyright 2021 Simon Arlott  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 @@ -33,28 +34,25 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.  #    define DEBOUNCE 5  #endif +// Maximum debounce: 255ms +#if DEBOUNCE > UINT8_MAX +#    undef DEBOUNCE +#    define DEBOUNCE UINT8_MAX +#endif +  #define ROW_SHIFTER ((matrix_row_t)1) -#define debounce_counter_t uint8_t +typedef uint8_t debounce_counter_t; +#if DEBOUNCE > 0  static debounce_counter_t *debounce_counters; +static fast_timer_t        last_time;  static bool                counters_need_update; -#define DEBOUNCE_ELAPSED 251 -#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) - -static uint8_t wrapping_timer_read(void) { -    static uint16_t time        = 0; -    static uint8_t  last_result = 0; -    uint16_t        new_time    = timer_read(); -    uint16_t        diff        = new_time - time; -    time                        = new_time; -    last_result                 = (last_result + diff) % (MAX_DEBOUNCE + 1); -    return last_result; -} +#define DEBOUNCE_ELAPSED 0 -void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); -void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); +static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time); +static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);  // we use num_rows rather than MATRIX_ROWS to support split keyboards  void debounce_init(uint8_t num_rows) { @@ -67,27 +65,49 @@ void debounce_init(uint8_t num_rows) {      }  } +void debounce_free(void) { +    free(debounce_counters); +    debounce_counters = NULL; +} +  void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { -    uint8_t current_time = wrapping_timer_read(); +    bool updated_last = false; +      if (counters_need_update) { -        update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, current_time); +        fast_timer_t now = timer_read_fast(); +        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); + +        last_time = now; +        updated_last = true; +        if (elapsed_time > UINT8_MAX) { +            elapsed_time = UINT8_MAX; +        } + +        if (elapsed_time > 0) { +            update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time); +        }      }      if (changed) { -        start_debounce_counters(raw, cooked, num_rows, current_time); +        if (!updated_last) { +            last_time = timer_read_fast(); +        } + +        start_debounce_counters(raw, cooked, num_rows);      }  } -void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { +static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) {      counters_need_update                 = false;      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) { +                if (*debounce_pointer <= elapsed_time) {                      *debounce_pointer = DEBOUNCE_ELAPSED;                      cooked[row]       = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col));                  } else { +                    *debounce_pointer -= elapsed_time;                      counters_need_update = true;                  }              } @@ -96,14 +116,14 @@ void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix      }  } -void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { +static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {      debounce_counter_t *debounce_pointer = debounce_counters;      for (uint8_t row = 0; row < num_rows; row++) {          matrix_row_t delta = raw[row] ^ cooked[row];          for (uint8_t col = 0; col < MATRIX_COLS; col++) {              if (delta & (ROW_SHIFTER << col)) {                  if (*debounce_pointer == DEBOUNCE_ELAPSED) { -                    *debounce_pointer    = current_time; +                    *debounce_pointer    = DEBOUNCE;                      counters_need_update = true;                  }              } else { @@ -115,3 +135,6 @@ void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t  }  bool debounce_active(void) { return true; } +#else +#    include "none.c" +#endif diff --git a/quantum/debounce/sym_eager_pk.c b/quantum/debounce/sym_eager_pk.c index e66cf92d79..15a3242e68 100644 --- a/quantum/debounce/sym_eager_pk.c +++ b/quantum/debounce/sym_eager_pk.c @@ -1,5 +1,6 @@  /*  Copyright 2017 Alex Ong<the.onga@gmail.com> +Copyright 2021 Simon Arlott  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 @@ -33,29 +34,26 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.  #    define DEBOUNCE 5  #endif +// Maximum debounce: 255ms +#if DEBOUNCE > UINT8_MAX +#    undef DEBOUNCE +#    define DEBOUNCE UINT8_MAX +#endif +  #define ROW_SHIFTER ((matrix_row_t)1) -#define debounce_counter_t uint8_t +typedef uint8_t debounce_counter_t; +#if DEBOUNCE > 0  static debounce_counter_t *debounce_counters; +static fast_timer_t        last_time;  static bool                counters_need_update;  static bool                matrix_need_update; -#define DEBOUNCE_ELAPSED 251 -#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) - -static uint8_t wrapping_timer_read(void) { -    static uint16_t time        = 0; -    static uint8_t  last_result = 0; -    uint16_t        new_time    = timer_read(); -    uint16_t        diff        = new_time - time; -    time                        = new_time; -    last_result                 = (last_result + diff) % (MAX_DEBOUNCE + 1); -    return last_result; -} +#define DEBOUNCE_ELAPSED 0 -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); +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);  // we use num_rows rather than MATRIX_ROWS to support split keyboards  void debounce_init(uint8_t num_rows) { @@ -68,27 +66,51 @@ void debounce_init(uint8_t num_rows) {      }  } +void debounce_free(void) { +    free(debounce_counters); +    debounce_counters = NULL; +} +  void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { -    uint8_t current_time = wrapping_timer_read(); +    bool updated_last = false; +      if (counters_need_update) { -        update_debounce_counters(num_rows, current_time); +        fast_timer_t now = timer_read_fast(); +        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); + +        last_time = now; +        updated_last = true; +        if (elapsed_time > UINT8_MAX) { +            elapsed_time = UINT8_MAX; +        } + +        if (elapsed_time > 0) { +            update_debounce_counters(num_rows, elapsed_time); +        }      }      if (changed || matrix_need_update) { -        transfer_matrix_values(raw, cooked, num_rows, current_time); +        if (!updated_last) { +            last_time = timer_read_fast(); +        } + +        transfer_matrix_values(raw, cooked, num_rows);      }  }  // 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) { +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {      counters_need_update                 = false; +    matrix_need_update                   = false;      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) { +                if (*debounce_pointer <= elapsed_time) {                      *debounce_pointer = DEBOUNCE_ELAPSED; +                    matrix_need_update = true;                  } else { +                    *debounce_pointer -= elapsed_time;                      counters_need_update = true;                  }              } @@ -98,8 +120,7 @@ void update_debounce_counters(uint8_t num_rows, uint8_t current_time) {  }  // 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) { -    matrix_need_update                   = false; +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {      debounce_counter_t *debounce_pointer = debounce_counters;      for (uint8_t row = 0; row < num_rows; row++) {          matrix_row_t delta        = raw[row] ^ cooked[row]; @@ -108,11 +129,9 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n              matrix_row_t col_mask = (ROW_SHIFTER << col);              if (delta & col_mask) {                  if (*debounce_pointer == DEBOUNCE_ELAPSED) { -                    *debounce_pointer    = current_time; +                    *debounce_pointer    = DEBOUNCE;                      counters_need_update = true;                      existing_row ^= col_mask;  // flip the bit. -                } else { -                    matrix_need_update = true;                  }              }              debounce_pointer++; @@ -122,3 +141,6 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n  }  bool debounce_active(void) { return true; } +#else +#    include "none.c" +#endif diff --git a/quantum/debounce/sym_eager_pr.c b/quantum/debounce/sym_eager_pr.c index 20ccb46f1d..2ad592c5a6 100644 --- a/quantum/debounce/sym_eager_pr.c +++ b/quantum/debounce/sym_eager_pr.c @@ -1,5 +1,6 @@  /*  Copyright 2019 Alex Ong<the.onga@gmail.com> +Copyright 2021 Simon Arlott  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 @@ -33,27 +34,25 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.  #    define DEBOUNCE 5  #endif -#define debounce_counter_t uint8_t +// Maximum debounce: 255ms +#if DEBOUNCE > UINT8_MAX +#    undef DEBOUNCE +#    define DEBOUNCE UINT8_MAX +#endif + +typedef uint8_t debounce_counter_t; + +#if DEBOUNCE > 0  static bool matrix_need_update;  static debounce_counter_t *debounce_counters; +static fast_timer_t        last_time;  static bool                counters_need_update; -#define DEBOUNCE_ELAPSED 251 -#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) - -static uint8_t wrapping_timer_read(void) { -    static uint16_t time        = 0; -    static uint8_t  last_result = 0; -    uint16_t        new_time    = timer_read(); -    uint16_t        diff        = new_time - time; -    time                        = new_time; -    last_result                 = (last_result + diff) % (MAX_DEBOUNCE + 1); -    return last_result; -} +#define DEBOUNCE_ELAPSED 0 -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); +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);  // we use num_rows rather than MATRIX_ROWS to support split keyboards  void debounce_init(uint8_t num_rows) { @@ -63,27 +62,50 @@ void debounce_init(uint8_t num_rows) {      }  } +void debounce_free(void) { +    free(debounce_counters); +    debounce_counters = NULL; +} +  void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { -    uint8_t current_time  = wrapping_timer_read(); -    bool    needed_update = counters_need_update; +    bool updated_last = false; +      if (counters_need_update) { -        update_debounce_counters(num_rows, current_time); +        fast_timer_t now = timer_read_fast(); +        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); + +        last_time = now; +        updated_last = true; +        if (elapsed_time > UINT8_MAX) { +            elapsed_time = UINT8_MAX; +        } + +        if (elapsed_time > 0) { +            update_debounce_counters(num_rows, elapsed_time); +        }      } -    if (changed || (needed_update && !counters_need_update) || matrix_need_update) { -        transfer_matrix_values(raw, cooked, num_rows, current_time); +    if (changed || matrix_need_update) { +        if (!updated_last) { +            last_time = timer_read_fast(); +        } + +        transfer_matrix_values(raw, cooked, num_rows);      }  }  // 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) { +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {      counters_need_update                 = false; +    matrix_need_update                   = false;      debounce_counter_t *debounce_pointer = debounce_counters;      for (uint8_t row = 0; row < num_rows; row++) {          if (*debounce_pointer != DEBOUNCE_ELAPSED) { -            if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { +            if (*debounce_pointer <= elapsed_time) {                  *debounce_pointer = DEBOUNCE_ELAPSED; +                matrix_need_update = true;              } else { +                *debounce_pointer -= elapsed_time;                  counters_need_update = true;              }          } @@ -92,8 +114,7 @@ void update_debounce_counters(uint8_t num_rows, uint8_t current_time) {  }  // 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) { -    matrix_need_update                   = false; +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {      debounce_counter_t *debounce_pointer = debounce_counters;      for (uint8_t row = 0; row < num_rows; row++) {          matrix_row_t existing_row = cooked[row]; @@ -102,11 +123,9 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n          // determine new value basd on debounce pointer + raw value          if (existing_row != raw_row) {              if (*debounce_pointer == DEBOUNCE_ELAPSED) { -                *debounce_pointer    = current_time; +                *debounce_pointer    = DEBOUNCE;                  cooked[row]          = raw_row;                  counters_need_update = true; -            } else { -                matrix_need_update = true;              }          }          debounce_pointer++; @@ -114,3 +133,6 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n  }  bool debounce_active(void) { return true; } +#else +#    include "none.c" +#endif diff --git a/quantum/debounce/tests/asym_eager_defer_pk_tests.cpp b/quantum/debounce/tests/asym_eager_defer_pk_tests.cpp new file mode 100644 index 0000000000..fe374c3dfa --- /dev/null +++ b/quantum/debounce/tests/asym_eager_defer_pk_tests.cpp @@ -0,0 +1,374 @@ +/* Copyright 2021 Simon Arlott + * + * 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/>. + */ + +#include "gtest/gtest.h" + +#include "debounce_test_common.h" + +TEST_F(DebounceTest, OneKeyShort1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        /* Release key after 1ms delay */ +        {1, {{0, 1, UP}}, {}}, + +        /* +         * Until the eager timer on DOWN is observed to finish, the defer timer +         * on UP can't start. There's no workaround for this because it's not +         * possible to debounce an event that isn't being tracked. +         * +         * sym_defer_pk has the same problem but the test has to track that the +         * key changed state so the DOWN timer is always allowed to finish +         * before starting the UP timer. +         */ +        {5, {}, {}}, + +        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ +        /* Press key again after 1ms delay */ +        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        /* Release key after 2ms delay */ +        {2, {{0, 1, UP}}, {}}, + +        {5, {}, {}}, /* See OneKeyShort1 */ + +        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ +        /* Press key again after 1ms delay */ +        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort3) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        /* Release key after 3ms delay */ +        {3, {{0, 1, UP}}, {}}, + +        {5, {}, {}}, /* See OneKeyShort1 */ + +        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ +        /* Press key again after 1ms delay */ +        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort4) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        /* Release key after 4ms delay */ +        {4, {{0, 1, UP}}, {}}, + +        {5, {}, {}}, /* See OneKeyShort1 */ + +        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ +        /* Press key again after 1ms delay */ +        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort5) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Release key after 5ms delay */ +        {5, {{0, 1, UP}}, {}}, + +        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ +        /* Press key again after 1ms delay */ +        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort6) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Release key after 6ms delay */ +        {6, {{0, 1, UP}}, {}}, + +        {11, {}, {{0, 1, UP}}}, /* 5ms after UP at time 6 */ +        /* Press key again after 1ms delay */ +        {12, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort7) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Release key after 7ms delay */ +        {7, {{0, 1, UP}}, {}}, + +        {12, {}, {{0, 1, UP}}}, /* 5ms after UP at time 7 */ +        /* Press key again after 1ms delay */ +        {13, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort8) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        /* Release key after 1ms delay */ +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {}}, /* See OneKeyShort1 */ + +        {10, {}, {{0, 1, UP}}}, /* 5ms after UP at time 7 */ +        /* Press key again after 0ms delay (scan 2) */ +        {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort9) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        /* Release key after 1ms delay */ +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {}}, /* See OneKeyShort1 */ + +        /* Press key again after 0ms delay (same scan) before debounce finishes */ +        {10, {{0, 1, DOWN}}, {}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, +        {2, {{0, 1, DOWN}}, {}}, +        {3, {{0, 1, UP}}, {}}, +        {4, {{0, 1, DOWN}}, {}}, +        {5, {{0, 1, UP}}, {}}, +        {6, {{0, 1, DOWN}}, {}}, +        {7, {{0, 1, UP}}, {}}, +        {8, {{0, 1, DOWN}}, {}}, +        {9, {{0, 1, UP}}, {}}, +        {10, {{0, 1, DOWN}}, {}}, +        {11, {{0, 1, UP}}, {}}, +        {12, {{0, 1, DOWN}}, {}}, +        {13, {{0, 1, UP}}, {}}, +        {14, {{0, 1, DOWN}}, {}}, +        {15, {{0, 1, UP}}, {}}, + +        {20, {}, {{0, 1, UP}}}, +        /* Press key again after 1ms delay */ +        {21, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        /* Change twice in the same time period */ +        {1, {{0, 1, UP}}, {}}, +        {1, {{0, 1, DOWN}}, {}}, +        /* Change three times in the same time period */ +        {2, {{0, 1, UP}}, {}}, +        {2, {{0, 1, DOWN}}, {}}, +        {2, {{0, 1, UP}}, {}}, +        /* Change twice in the same time period */ +        {6, {{0, 1, DOWN}}, {}}, +        {6, {{0, 1, UP}}, {}}, +        /* Change three times in the same time period */ +        {7, {{0, 1, DOWN}}, {}}, +        {7, {{0, 1, UP}}, {}}, +        {7, {{0, 1, DOWN}}, {}}, +        /* Change twice in the same time period */ +        {8, {{0, 1, UP}}, {}}, +        {8, {{0, 1, DOWN}}, {}}, +        /* Change three times in the same time period */ +        {9, {{0, 1, UP}}, {}}, +        {9, {{0, 1, DOWN}}, {}}, +        {9, {{0, 1, UP}}, {}}, + +        {14, {}, {{0, 1, UP}}}, +        /* Press key again after 1ms delay */ +        {15, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyLong) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        {25, {{0, 1, UP}}, {}}, + +        {30, {}, {{0, 1, UP}}}, + +        {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        {75, {{0, 1, UP}}, {}}, + +        {80, {}, {{0, 1, UP}}}, + +        {100, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoKeysShort) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 2, DOWN}}, {{0, 2, DOWN}}}, +        /* Release key after 2ms delay */ +        {2, {{0, 1, UP}}, {}}, +        {3, {{0, 2, UP}}, {}}, + +        {5, {}, {}}, /* See OneKeyShort1 */ +        {6, {}, {}}, /* See OneKeyShort1 */ + +        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */ +        /* Press key again after 1ms delay */ +        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}, {0, 2, UP}}}, /* 5ms+5ms after DOWN at time 0 */ +        {12, {{0, 2, DOWN}}, {{0, 2, DOWN}}}, /* 5ms+5ms after DOWN at time 0 */ +    }); +    runEvents(); +} + + +TEST_F(DebounceTest, OneKeyDelayedScan1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late, immediately release key */ +        {300, {{0, 1, UP}}, {}}, + +        {305, {}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late, immediately release key */ +        {300, {{0, 1, UP}}, {}}, + +        /* Processing is very late again */ +        {600, {}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan3) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late */ +        {300, {}, {}}, +        /* Release key after 1ms */ +        {301, {{0, 1, UP}}, {}}, + +        {306, {}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan4) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late */ +        {300, {}, {}}, +        /* Release key after 1ms */ +        {301, {{0, 1, UP}}, {}}, + +        /* Processing is very late again */ +        {600, {}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan5) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        {5, {{0, 1, UP}}, {}}, + +        /* Processing is very late */ +        {300, {}, {{0, 1, UP}}}, +        /* Immediately press key again */ +        {300, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan6) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        {5, {{0, 1, UP}}, {}}, + +        /* Processing is very late */ +        {300, {}, {{0, 1, UP}}}, + +        /* Press key again after 1ms */ +        {301, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan7) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        {5, {{0, 1, UP}}, {}}, + +        /* Press key again before debounce expires */ +        {300, {{0, 1, DOWN}}, {}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan8) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is a bit late */ +        {50, {}, {}}, +        /* Release key after 1ms */ +        {51, {{0, 1, UP}}, {}}, + +        /* Processing is a bit late again */ +        {100, {}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} diff --git a/quantum/debounce/tests/debounce_test_common.cpp b/quantum/debounce/tests/debounce_test_common.cpp new file mode 100644 index 0000000000..1c5e7c9f4e --- /dev/null +++ b/quantum/debounce/tests/debounce_test_common.cpp @@ -0,0 +1,229 @@ +/* Copyright 2021 Simon Arlott + * + * 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/>. + */ + +#include "gtest/gtest.h" + +#include "debounce_test_common.h" + +#include <algorithm> +#include <iomanip> +#include <sstream> + +extern "C" { +#include "quantum.h" +#include "timer.h" +#include "debounce.h" + +void set_time(uint32_t t); +void advance_time(uint32_t ms); +} + +void DebounceTest::addEvents(std::initializer_list<DebounceTestEvent> events) { +    events_.insert(events_.end(), events.begin(), events.end()); +} + +void DebounceTest::runEvents() { +    /* Run the test multiple times, from 1kHz to 10kHz scan rate */ +    for (extra_iterations_ = 0; extra_iterations_ < 10; extra_iterations_++) { +        if (time_jumps_) { +            /* Don't advance time smoothly, jump to the next event (some tests require this) */ +            auto_advance_time_ = false; +            runEventsInternal(); +        } else { +            /* Run the test with both smooth and irregular time; it must produce the same result */ +            auto_advance_time_ = true; +            runEventsInternal(); +            auto_advance_time_ = false; +            runEventsInternal(); +        } +    } +} + +void DebounceTest::runEventsInternal() { +    fast_timer_t previous = 0; +    bool first = true; + +    /* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */ +    debounce_init(MATRIX_ROWS); +    set_time(time_offset_); +    std::fill(std::begin(input_matrix_), std::end(input_matrix_), 0); +    std::fill(std::begin(output_matrix_), std::end(output_matrix_), 0); + +    for (auto &event : events_) { +        if (!auto_advance_time_) { +            /* Jump to the next event */ +            set_time(time_offset_ + event.time_); +        } else if (!first && event.time_ == previous + 1) { +            /* This event immediately follows the previous one, don't make extra debounce() calls */ +            advance_time(1); +        } else { +            /* Fast forward to the time for this event, calling debounce() with no changes */ +            ASSERT_LT((time_offset_ + event.time_) - timer_read_fast(), 60000) << "Test tries to advance more than 1 minute of time"; + +            while (timer_read_fast() != time_offset_ + event.time_) { +                runDebounce(false); +                checkCookedMatrix(false, "debounce() modified cooked matrix"); +                advance_time(1); +            } +        } + +        first = false; +        previous = event.time_; + +        /* Prepare input matrix */ +        for (auto &input : event.inputs_) { +            matrixUpdate(input_matrix_, "input", input); +        } + +        /* Call debounce */ +        runDebounce(!event.inputs_.empty()); + +        /* Prepare output matrix */ +        for (auto &output : event.outputs_) { +            matrixUpdate(output_matrix_, "output", output); +        } + +        /* Check output matrix has expected change events */ +        for (auto &output : event.outputs_) { +            EXPECT_EQ(!!(cooked_matrix_[output.row_] & (1U << output.col_)), directionValue(output.direction_)) +                    << "Missing event at " << strTime() +                    << " expected key " << output.row_ << "," << output.col_ << " " << directionLabel(output.direction_) +                    << "\ninput_matrix: changed=" << !event.inputs_.empty() << "\n" << strMatrix(input_matrix_) +                    << "\nexpected_matrix:\n" << strMatrix(output_matrix_) +                    << "\nactual_matrix:\n" << strMatrix(cooked_matrix_); +        } + +        /* Check output matrix has no other changes */ +        checkCookedMatrix(!event.inputs_.empty(), "debounce() cooked matrix does not match expected output matrix"); + +        /* Perform some extra iterations of the matrix scan with no changes */ +        for (int i = 0; i < extra_iterations_; i++) { +            runDebounce(false); +            checkCookedMatrix(false, "debounce() modified cooked matrix"); +        } +    } + +    /* Check that no further changes happen for 1 minute */ +    for (int i = 0; i < 60000; i++) { +        runDebounce(false); +        checkCookedMatrix(false, "debounce() modified cooked matrix"); +        advance_time(1); +    } + +    debounce_free(); +} + +void DebounceTest::runDebounce(bool changed) { +    std::copy(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_)); +    std::copy(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_)); + +    debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed); + +    if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) { +        FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime() +            << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) +            << "\nraw_matrix:\n" << strMatrix(raw_matrix_); +    } +} + +void DebounceTest::checkCookedMatrix(bool changed, const std::string &error_message) { +    if (!std::equal(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_))) { +        FAIL() << "Unexpected event: " << error_message << " at " << strTime() +            << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) +            << "\nexpected_matrix:\n" << strMatrix(output_matrix_) +            << "\nactual_matrix:\n" << strMatrix(cooked_matrix_); +    } +} + +std::string DebounceTest::strTime() { +    std::stringstream text; + +    text << "time " << (timer_read_fast() - time_offset_) +        << " (extra_iterations=" << extra_iterations_ +        << ", auto_advance_time=" << auto_advance_time_ << ")"; + +    return text.str(); +} + +std::string DebounceTest::strMatrix(matrix_row_t matrix[]) { +    std::stringstream text; + +    text << "\t" << std::setw(3) << ""; +    for (int col = 0; col < MATRIX_COLS; col++) { +        text << " " << std::setw(2) << col; +    } +    text << "\n"; + +    for (int row = 0; row < MATRIX_ROWS; row++) { +        text << "\t" << std::setw(2) << row << ":"; +        for (int col = 0; col < MATRIX_COLS; col++) { +            text << ((matrix[row] & (1U << col)) ? " XX" : " __"); +        } + +        text << "\n"; +    } + +    return text.str(); +} + +bool DebounceTest::directionValue(Direction direction) { +    switch (direction) { +    case DOWN: +        return true; + +    case UP: +        return false; +    } +} + +std::string DebounceTest::directionLabel(Direction direction) { +    switch (direction) { +    case DOWN: +        return "DOWN"; + +    case UP: +        return "UP"; +    } +} + +/* Modify a matrix and verify that events always specify a change */ +void DebounceTest::matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event) { +    ASSERT_NE(!!(matrix[event.row_] & (1U << event.col_)), directionValue(event.direction_)) +        << "Test " << name << " at " << strTime() +        << " sets key " << event.row_ << "," << event.col_ << " " << directionLabel(event.direction_) +        << " but it is already " << directionLabel(event.direction_) +        << "\n" << name << "_matrix:\n" << strMatrix(matrix); + +    switch (event.direction_) { +    case DOWN: +        matrix[event.row_] |= (1U << event.col_); +        break; + +    case UP: +        matrix[event.row_] &= ~(1U << event.col_); +        break; +    } +} + +DebounceTestEvent::DebounceTestEvent(fast_timer_t time, +        std::initializer_list<MatrixTestEvent> inputs, +        std::initializer_list<MatrixTestEvent> outputs) +        : time_(time), inputs_(inputs), outputs_(outputs) { +} + +MatrixTestEvent::MatrixTestEvent(int row, int col, Direction direction) +        : row_(row), col_(col), direction_(direction) { +} diff --git a/quantum/debounce/tests/debounce_test_common.h b/quantum/debounce/tests/debounce_test_common.h new file mode 100644 index 0000000000..d87e310594 --- /dev/null +++ b/quantum/debounce/tests/debounce_test_common.h @@ -0,0 +1,83 @@ +/* Copyright 2021 Simon Arlott + * + * 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/>. + */ + +#include "gtest/gtest.h" + +#include <initializer_list> +#include <list> +#include <string> + +extern "C" { +#include "quantum.h" +#include "timer.h" +} + +enum Direction { +    DOWN, +    UP, +}; + +class MatrixTestEvent { +public: +    MatrixTestEvent(int row, int col, Direction direction); + +    const int row_; +    const int col_; +    const Direction direction_; +}; + +class DebounceTestEvent { +public: +    // 0, {{0, 1, DOWN}}, {{0, 1, DOWN}}) +    DebounceTestEvent(fast_timer_t time, +        std::initializer_list<MatrixTestEvent> inputs, +        std::initializer_list<MatrixTestEvent> outputs); + +    const fast_timer_t time_; +    const std::list<MatrixTestEvent> inputs_; +    const std::list<MatrixTestEvent> outputs_; +}; + +class DebounceTest : public ::testing::Test { +protected: +    void addEvents(std::initializer_list<DebounceTestEvent> events); +    void runEvents(); + +    fast_timer_t time_offset_ = 7777; +    bool time_jumps_ = false; + +private: +    static bool directionValue(Direction direction); +    static std::string directionLabel(Direction direction); + +    void runEventsInternal(); +    void runDebounce(bool changed); +    void checkCookedMatrix(bool changed, const std::string &error_message); +    void matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event); + +    std::string strTime(); +    std::string strMatrix(matrix_row_t matrix[]); + +    std::list<DebounceTestEvent> events_; + +    matrix_row_t input_matrix_[MATRIX_ROWS]; +    matrix_row_t raw_matrix_[MATRIX_ROWS]; +    matrix_row_t cooked_matrix_[MATRIX_ROWS]; +    matrix_row_t output_matrix_[MATRIX_ROWS]; + +    int extra_iterations_; +    bool auto_advance_time_; +}; diff --git a/quantum/debounce/tests/rules.mk b/quantum/debounce/tests/rules.mk new file mode 100644 index 0000000000..66928d7eb6 --- /dev/null +++ b/quantum/debounce/tests/rules.mk @@ -0,0 +1,44 @@ +# Copyright 2021 Simon Arlott +# +# 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/>. + +DEBOUNCE_COMMON_DEFS := -DMATRIX_ROWS=4 -DMATRIX_COLS=10 -DDEBOUNCE=5 + +DEBOUNCE_COMMON_SRC := $(QUANTUM_PATH)/debounce/tests/debounce_test_common.cpp \ +	$(TMK_PATH)/common/test/timer.c + +debounce_sym_defer_g_DEFS := $(DEBOUNCE_COMMON_DEFS) +debounce_sym_defer_g_SRC := $(DEBOUNCE_COMMON_SRC) \ +	$(QUANTUM_PATH)/debounce/sym_defer_g.c \ +	$(QUANTUM_PATH)/debounce/tests/sym_defer_g_tests.cpp + +debounce_sym_defer_pk_DEFS := $(DEBOUNCE_COMMON_DEFS) +debounce_sym_defer_pk_SRC := $(DEBOUNCE_COMMON_SRC) \ +	$(QUANTUM_PATH)/debounce/sym_defer_pk.c \ +	$(QUANTUM_PATH)/debounce/tests/sym_defer_pk_tests.cpp + +debounce_sym_eager_pk_DEFS := $(DEBOUNCE_COMMON_DEFS) +debounce_sym_eager_pk_SRC := $(DEBOUNCE_COMMON_SRC) \ +	$(QUANTUM_PATH)/debounce/sym_eager_pk.c \ +	$(QUANTUM_PATH)/debounce/tests/sym_eager_pk_tests.cpp + +debounce_sym_eager_pr_DEFS := $(DEBOUNCE_COMMON_DEFS) +debounce_sym_eager_pr_SRC := $(DEBOUNCE_COMMON_SRC) \ +	$(QUANTUM_PATH)/debounce/sym_eager_pr.c \ +	$(QUANTUM_PATH)/debounce/tests/sym_eager_pr_tests.cpp + +debounce_asym_eager_defer_pk_DEFS := $(DEBOUNCE_COMMON_DEFS) +debounce_asym_eager_defer_pk_SRC := $(DEBOUNCE_COMMON_SRC) \ +	$(QUANTUM_PATH)/debounce/asym_eager_defer_pk.c \ +	$(QUANTUM_PATH)/debounce/tests/asym_eager_defer_pk_tests.cpp diff --git a/quantum/debounce/tests/sym_defer_g_tests.cpp b/quantum/debounce/tests/sym_defer_g_tests.cpp new file mode 100644 index 0000000000..a56aecd8f3 --- /dev/null +++ b/quantum/debounce/tests/sym_defer_g_tests.cpp @@ -0,0 +1,223 @@ +/* Copyright 2021 Simon Arlott + * + * 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/>. + */ + +#include "gtest/gtest.h" + +#include "debounce_test_common.h" + +TEST_F(DebounceTest, OneKeyShort1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, +        /* 0ms delay (fast scan rate) */ +        {5, {{0, 1, UP}}, {}}, + +        {10, {}, {{0, 1, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, +        /* 1ms delay */ +        {6, {{0, 1, UP}}, {}}, + +        {11, {}, {{0, 1, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort3) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, +        /* 2ms delay */ +        {7, {{0, 1, UP}}, {}}, + +        {12, {}, {{0, 1, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyTooQuick1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, +        /* Release key exactly on the debounce time */ +        {5, {{0, 1, UP}}, {}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyTooQuick2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, +        {6, {{0, 1, UP}}, {}}, + +        /* Press key exactly on the debounce time */ +        {11, {{0, 1, DOWN}}, {}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, +        {1, {{0, 1, UP}}, {}}, +        {2, {{0, 1, DOWN}}, {}}, +        {3, {{0, 1, UP}}, {}}, +        {4, {{0, 1, DOWN}}, {}}, +        {5, {{0, 1, UP}}, {}}, +        {6, {{0, 1, DOWN}}, {}}, +        {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, +        {5, {}, {{0, 1, DOWN}}}, +        {6, {{0, 1, UP}}, {}}, +        {7, {{0, 1, DOWN}}, {}}, +        {8, {{0, 1, UP}}, {}}, +        {9, {{0, 1, DOWN}}, {}}, +        {10, {{0, 1, UP}}, {}}, +        {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyLong) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, + +        {25, {{0, 1, UP}}, {}}, + +        {30, {}, {{0, 1, UP}}}, + +        {50, {{0, 1, DOWN}}, {}}, + +        {55, {}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoKeysShort) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, +        {1, {{0, 2, DOWN}}, {}}, + +        {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}}, + +        {7, {{0, 1, UP}}, {}}, +        {8, {{0, 2, UP}}, {}}, + +        {13, {}, {{0, 1, UP}, {0, 2, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}}, +        {6, {{0, 1, UP}, {0, 2, UP}}, {}}, + +        {11, {}, {{0, 1, UP}, {0, 2, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, +        {1, {{0, 2, DOWN}}, {}}, + +        {5, {}, {}}, +        {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}}, +        {7, {{0, 1, UP}}, {}}, +        {8, {{0, 2, UP}}, {}}, + +        {13, {}, {{0, 1, UP}, {0, 2, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        /* Processing is very late */ +        {300, {}, {{0, 1, DOWN}}}, +        /* Immediately release key */ +        {300, {{0, 1, UP}}, {}}, + +        {305, {}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        /* Processing is very late */ +        {300, {}, {{0, 1, DOWN}}}, +        /* Release key after 1ms */ +        {301, {{0, 1, UP}}, {}}, + +        {306, {}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan3) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        /* Release key before debounce expires */ +        {300, {{0, 1, UP}}, {}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan4) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        /* Processing is a bit late */ +        {50, {}, {{0, 1, DOWN}}}, +        /* Release key after 1ms */ +        {51, {{0, 1, UP}}, {}}, + +        {56, {}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} diff --git a/quantum/debounce/tests/sym_defer_pk_tests.cpp b/quantum/debounce/tests/sym_defer_pk_tests.cpp new file mode 100644 index 0000000000..1f3061e59c --- /dev/null +++ b/quantum/debounce/tests/sym_defer_pk_tests.cpp @@ -0,0 +1,225 @@ +/* Copyright 2021 Simon Arlott + * + * 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/>. + */ + +#include "gtest/gtest.h" + +#include "debounce_test_common.h" + +TEST_F(DebounceTest, OneKeyShort1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, +        /* 0ms delay (fast scan rate) */ +        {5, {{0, 1, UP}}, {}}, + +        {10, {}, {{0, 1, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, +        /* 1ms delay */ +        {6, {{0, 1, UP}}, {}}, + +        {11, {}, {{0, 1, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort3) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, +        /* 2ms delay */ +        {7, {{0, 1, UP}}, {}}, + +        {12, {}, {{0, 1, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyTooQuick1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, +        /* Release key exactly on the debounce time */ +        {5, {{0, 1, UP}}, {}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyTooQuick2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, +        {6, {{0, 1, UP}}, {}}, + +        /* Press key exactly on the debounce time */ +        {11, {{0, 1, DOWN}}, {}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, +        {1, {{0, 1, UP}}, {}}, +        {2, {{0, 1, DOWN}}, {}}, +        {3, {{0, 1, UP}}, {}}, +        {4, {{0, 1, DOWN}}, {}}, +        {5, {{0, 1, UP}}, {}}, +        {6, {{0, 1, DOWN}}, {}}, +        {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, +        {5, {}, {{0, 1, DOWN}}}, +        {6, {{0, 1, UP}}, {}}, +        {7, {{0, 1, DOWN}}, {}}, +        {8, {{0, 1, UP}}, {}}, +        {9, {{0, 1, DOWN}}, {}}, +        {10, {{0, 1, UP}}, {}}, +        {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyLong) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, + +        {25, {{0, 1, UP}}, {}}, + +        {30, {}, {{0, 1, UP}}}, + +        {50, {{0, 1, DOWN}}, {}}, + +        {55, {}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoKeysShort) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, +        {1, {{0, 2, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, +        {6, {}, {{0, 2, DOWN}}}, + +        {7, {{0, 1, UP}}, {}}, +        {8, {{0, 2, UP}}, {}}, + +        {12, {}, {{0, 1, UP}}}, +        {13, {}, {{0, 2, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}}, +        {6, {{0, 1, UP}, {0, 2, UP}}, {}}, + +        {11, {}, {{0, 1, UP}, {0, 2, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, +        {1, {{0, 2, DOWN}}, {}}, + +        {5, {}, {{0, 1, DOWN}}}, +        {6, {{0, 1, UP}}, {{0, 2, DOWN}}}, +        {7, {{0, 2, UP}}, {}}, + +        {11, {}, {{0, 1, UP}}}, +        {12, {}, {{0, 2, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        /* Processing is very late */ +        {300, {}, {{0, 1, DOWN}}}, +        /* Immediately release key */ +        {300, {{0, 1, UP}}, {}}, + +        {305, {}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        /* Processing is very late */ +        {300, {}, {{0, 1, DOWN}}}, +        /* Release key after 1ms */ +        {301, {{0, 1, UP}}, {}}, + +        {306, {}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan3) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        /* Release key before debounce expires */ +        {300, {{0, 1, UP}}, {}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan4) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {}}, + +        /* Processing is a bit late */ +        {50, {}, {{0, 1, DOWN}}}, +        /* Release key after 1ms */ +        {51, {{0, 1, UP}}, {}}, + +        {56, {}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} diff --git a/quantum/debounce/tests/sym_eager_pk_tests.cpp b/quantum/debounce/tests/sym_eager_pk_tests.cpp new file mode 100644 index 0000000000..e0fc205e33 --- /dev/null +++ b/quantum/debounce/tests/sym_eager_pk_tests.cpp @@ -0,0 +1,237 @@ +/* Copyright 2021 Simon Arlott + * + * 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/>. + */ + +#include "gtest/gtest.h" + +#include "debounce_test_common.h" + +TEST_F(DebounceTest, OneKeyShort1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 1ms delay (debounce has not yet finished) */ +        {6, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 2ms delay (debounce has not yet finished) */ +        {7, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort3) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 3ms delay (debounce has not yet finished) */ +        {8, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort4) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 4ms delay (debounce has not yet finished) */ +        {9, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort5) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 5ms delay (debounce has finished) */ +        {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort6) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key after after 6ms delay (debounce has finished) */ +        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, +        {2, {{0, 1, DOWN}}, {}}, +        {3, {{0, 1, UP}}, {}}, +        {4, {{0, 1, DOWN}}, {}}, +        {5, {{0, 1, UP}}, {{0, 1, UP}}}, +        /* Press key again after 1ms delay (debounce has not yet finished) */ +        {6, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        /* Change twice in the same time period */ +        {1, {{0, 1, UP}}, {}}, +        {1, {{0, 1, DOWN}}, {}}, +        /* Change three times in the same time period */ +        {2, {{0, 1, UP}}, {}}, +        {2, {{0, 1, DOWN}}, {}}, +        {2, {{0, 1, UP}}, {}}, +        /* Change three times in the same time period */ +        {3, {{0, 1, DOWN}}, {}}, +        {3, {{0, 1, UP}}, {}}, +        {3, {{0, 1, DOWN}}, {}}, +        /* Change twice in the same time period */ +        {4, {{0, 1, UP}}, {}}, +        {4, {{0, 1, DOWN}}, {}}, +        {5, {{0, 1, UP}}, {{0, 1, UP}}}, +        /* Press key again after 1ms delay (debounce has not yet finished) */ +        {6, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyLong) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        {25, {{0, 1, UP}}, {{0, 1, UP}}}, + +        {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoKeysShort) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, +        {2, {{0, 2, DOWN}}, {{0, 2, DOWN}}}, +        {3, {{0, 2, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 1ms delay (debounce has not yet finished) */ +        {6, {{0, 1, DOWN}}, {}}, +        {7, {}, {{0, 2, UP}}}, + +        /* Press key again after 1ms delay (debounce has not yet finished) */ +        {9, {{0, 2, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + +        {12, {}, {{0, 2, DOWN}}}, /* 5ms after UP at time 7 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late but the change will now be accepted */ +        {300, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late but the change will now be accepted even with a 1 scan delay */ +        {300, {}, {}}, +        {300, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan3) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late but the change will now be accepted even with a 1ms delay */ +        {300, {}, {}}, +        {301, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan4) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is a bit late but the change will now be accepted */ +        {50, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan5) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late but the change will now be accepted even with a 1 scan delay */ +        {50, {}, {}}, +        {50, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan6) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late but the change will now be accepted even with a 1ms delay */ +        {50, {}, {}}, +        {51, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} diff --git a/quantum/debounce/tests/sym_eager_pr_tests.cpp b/quantum/debounce/tests/sym_eager_pr_tests.cpp new file mode 100644 index 0000000000..2c4bca127e --- /dev/null +++ b/quantum/debounce/tests/sym_eager_pr_tests.cpp @@ -0,0 +1,280 @@ +/* Copyright 2021 Simon Arlott + * + * 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/>. + */ + +#include "gtest/gtest.h" + +#include "debounce_test_common.h" + +TEST_F(DebounceTest, OneKeyShort1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 1ms delay (debounce has not yet finished) */ +        {6, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 2ms delay (debounce has not yet finished) */ +        {7, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort3) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 3ms delay (debounce has not yet finished) */ +        {8, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort4) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 4ms delay (debounce has not yet finished) */ +        {9, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort5) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 5ms delay (debounce has finished) */ +        {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort6) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key after after 6ms delay (debounce has finished) */ +        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, +        {2, {{0, 1, DOWN}}, {}}, +        {3, {{0, 1, UP}}, {}}, +        {4, {{0, 1, DOWN}}, {}}, +        {5, {{0, 1, UP}}, {{0, 1, UP}}}, +        /* Press key again after 1ms delay (debounce has not yet finished) */ +        {6, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        /* Change twice in the same time period */ +        {1, {{0, 1, UP}}, {}}, +        {1, {{0, 1, DOWN}}, {}}, +        /* Change three times in the same time period */ +        {2, {{0, 1, UP}}, {}}, +        {2, {{0, 1, DOWN}}, {}}, +        {2, {{0, 1, UP}}, {}}, +        /* Change three times in the same time period */ +        {3, {{0, 1, DOWN}}, {}}, +        {3, {{0, 1, UP}}, {}}, +        {3, {{0, 1, DOWN}}, {}}, +        /* Change twice in the same time period */ +        {4, {{0, 1, UP}}, {}}, +        {4, {{0, 1, DOWN}}, {}}, +        {5, {{0, 1, UP}}, {{0, 1, UP}}}, +        /* Press key again after 1ms delay (debounce has not yet finished) */ +        {6, {{0, 1, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyLong) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        {25, {{0, 1, UP}}, {{0, 1, UP}}}, + +        {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoRowsShort) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, +        {2, {{2, 0, DOWN}}, {{2, 0, DOWN}}}, +        {3, {{2, 0, UP}}, {}}, + +        {5, {}, {{0, 1, UP}}}, +        /* Press key again after 1ms delay (debounce has not yet finished) */ +        {6, {{0, 1, DOWN}}, {}}, +        {7, {}, {{2, 0, UP}}}, + +        /* Press key again after 1ms delay (debounce has not yet finished) */ +        {9, {{2, 0, DOWN}}, {}}, +        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + +        {12, {}, {{2, 0, DOWN}}}, /* 5ms after UP at time 7 */ +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoKeysOverlap) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, +        {1, {{0, 1, UP}}, {}}, +        /* Press a second key during the first debounce */ +        {2, {{0, 2, DOWN}}, {}}, + +        /* Key registers as soon as debounce finishes, 5ms after time 0 */ +        {5, {}, {{0, 1, UP}, {0, 2, DOWN}}}, +        {6, {{0, 1, DOWN}}, {}}, + +        /* Key registers as soon as debounce finishes, 5ms after time 5 */ +        {10, {}, {{0, 1, DOWN}}}, +        /* Release both keys */ +        {11, {{0, 1, UP}}, {}}, +        {12, {{0, 2, UP}}, {}}, + +        /* Keys register as soon as debounce finishes, 5ms after time 10 */ +        {15, {}, {{0, 1, UP}, {0, 2, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}}, +        {20, {{0, 1, UP}}, {{0, 1, UP}}}, +        {21, {{0, 2, UP}}, {}}, + +        /* Key registers as soon as debounce finishes, 5ms after time 20 */ +        {25, {}, {{0, 2, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}}, +        {20, {{0, 1, UP}, {0, 2, UP}}, {{0, 1, UP}, {0, 2, UP}}}, +    }); +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan1) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late but the change will now be accepted */ +        {300, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan2) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late but the change will now be accepted even with a 1 scan delay */ +        {300, {}, {}}, +        {300, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan3) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late but the change will now be accepted even with a 1ms delay */ +        {300, {}, {}}, +        {301, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan4) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is a bit late but the change will now be accepted */ +        {50, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan5) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late but the change will now be accepted even with a 1 scan delay */ +        {50, {}, {}}, +        {50, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan6) { +    addEvents({ /* Time, Inputs, Outputs */ +        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + +        /* Processing is very late but the change will now be accepted even with a 1ms delay */ +        {50, {}, {}}, +        {51, {{0, 1, UP}}, {{0, 1, UP}}}, +    }); +    time_jumps_ = true; +    runEvents(); +} diff --git a/quantum/debounce/tests/testlist.mk b/quantum/debounce/tests/testlist.mk new file mode 100644 index 0000000000..c54c45aa63 --- /dev/null +++ b/quantum/debounce/tests/testlist.mk @@ -0,0 +1,6 @@ +TEST_LIST += \ +	debounce_sym_defer_g \ +	debounce_sym_defer_pk \ +	debounce_sym_eager_pk \ +	debounce_sym_eager_pr \ +	debounce_asym_eager_defer_pk | 
