mirror of
				https://github.com/mfulz/qmk_firmware.git
				synced 2025-10-30 21:02:32 +01:00 
			
		
		
		
	Avoid 8-bit timer overflows in debounce algorithms (#12240)
* Add fast_timer_t that is 16-bit or 32-bit based on architecture A 16-bit timer will overflow sooner but be faster to compare on AVR. * Avoid 8-bit timer overflows in debounce algorithms Count down remaining elapsed time instead of trying to do 8-bit timer comparisons. Add a "none" implementation that is automatically used if DEBOUNCE is 0 otherwise it will break the _pk/_pr count down. * Avoid unnecessary polling of the entire matrix in sym_eager_pk The matrix only needs to be updated when a debounce timer expires. * Avoid unnecessary polling of the entire matrix in sym_eager_pr The matrix only needs to be updated when a debounce timer expires. The use of the "needed_update" variable is trying to do what "matrix_need_update" was added to fix but didn't work because it only applied when all keys finished debouncing. * Fix sym_defer_g timing inconsistency compared to other debounce algorithms DEBOUNCE=5 should process the key after 5ms, not 6ms * Add debounce tests
This commit is contained in:
		
							parent
							
								
									f287597c19
								
							
						
					
					
						commit
						b829a1d264
					
				| @ -49,6 +49,7 @@ endif | |||||||
| 
 | 
 | ||||||
| include common_features.mk | include common_features.mk | ||||||
| include $(TMK_PATH)/common.mk | include $(TMK_PATH)/common.mk | ||||||
|  | include $(QUANTUM_PATH)/debounce/tests/rules.mk | ||||||
| include $(QUANTUM_PATH)/sequencer/tests/rules.mk | include $(QUANTUM_PATH)/sequencer/tests/rules.mk | ||||||
| include $(QUANTUM_PATH)/serial_link/tests/rules.mk | include $(QUANTUM_PATH)/serial_link/tests/rules.mk | ||||||
| ifneq ($(filter $(FULL_TESTS),$(TEST)),) | ifneq ($(filter $(FULL_TESTS),$(TEST)),) | ||||||
|  | |||||||
| @ -9,3 +9,5 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool | |||||||
| bool debounce_active(void); | bool debounce_active(void); | ||||||
| 
 | 
 | ||||||
| void debounce_init(uint8_t num_rows); | void debounce_init(uint8_t num_rows); | ||||||
|  | 
 | ||||||
|  | void debounce_free(void); | ||||||
|  | |||||||
							
								
								
									
										31
									
								
								quantum/debounce/none.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								quantum/debounce/none.c
									
									
									
									
									
										Normal file
									
								
							| @ -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) {} | ||||||
| @ -1,5 +1,6 @@ | |||||||
| /*
 | /*
 | ||||||
| Copyright 2017 Alex Ong<the.onga@gmail.com> | Copyright 2017 Alex Ong<the.onga@gmail.com> | ||||||
|  | Copyright 2021 Simon Arlott | ||||||
| This program is free software: you can redistribute it and/or modify | 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 | it under the terms of the GNU General Public License as published by | ||||||
| the Free Software Foundation, either version 2 of the License, or | 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 | #    define DEBOUNCE 5 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| void        debounce_init(uint8_t num_rows) {} |  | ||||||
| static bool debouncing = false; |  | ||||||
| 
 |  | ||||||
| #if DEBOUNCE > 0 | #if DEBOUNCE > 0 | ||||||
| static uint16_t debouncing_time; | static bool debouncing = false; | ||||||
|  | static fast_timer_t debouncing_time; | ||||||
|  | 
 | ||||||
|  | void debounce_init(uint8_t num_rows) {} | ||||||
|  | 
 | ||||||
| void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | ||||||
|     if (changed) { |     if (changed) { | ||||||
|         debouncing      = true; |         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++) { |         for (int i = 0; i < num_rows; i++) { | ||||||
|             cooked[i] = raw[i]; |             cooked[i] = raw[i]; | ||||||
|         } |         } | ||||||
|         debouncing = false; |         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; } | bool debounce_active(void) { return debouncing; } | ||||||
|  | 
 | ||||||
|  | void debounce_free(void) {} | ||||||
|  | #else  // no debouncing.
 | ||||||
|  | #    include "none.c" | ||||||
|  | #endif | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| /*
 | /*
 | ||||||
| Copyright 2017 Alex Ong<the.onga@gmail.com> | Copyright 2017 Alex Ong<the.onga@gmail.com> | ||||||
| Copyright 2020 Andrei Purdea<andrei@purdea.ro> | Copyright 2020 Andrei Purdea<andrei@purdea.ro> | ||||||
|  | Copyright 2021 Simon Arlott | ||||||
| This program is free software: you can redistribute it and/or modify | 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 | it under the terms of the GNU General Public License as published by | ||||||
| the Free Software Foundation, either version 2 of the License, or | 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 | #    define DEBOUNCE 5 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | // Maximum debounce: 255ms
 | ||||||
|  | #if DEBOUNCE > UINT8_MAX | ||||||
|  | #    undef DEBOUNCE | ||||||
|  | #    define DEBOUNCE UINT8_MAX | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #define ROW_SHIFTER ((matrix_row_t)1) | #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 debounce_counter_t *debounce_counters; | ||||||
|  | static fast_timer_t        last_time; | ||||||
| static bool                counters_need_update; | static bool                counters_need_update; | ||||||
| 
 | 
 | ||||||
| #define DEBOUNCE_ELAPSED 251 | #define DEBOUNCE_ELAPSED 0 | ||||||
| #define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) |  | ||||||
| 
 | 
 | ||||||
| static uint8_t wrapping_timer_read(void) { | 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 uint16_t time        = 0; | static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); | ||||||
|     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; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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); |  | ||||||
| 
 | 
 | ||||||
| // we use num_rows rather than MATRIX_ROWS to support split keyboards
 | // we use num_rows rather than MATRIX_ROWS to support split keyboards
 | ||||||
| void debounce_init(uint8_t num_rows) { | 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) { | 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) { |     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) { |     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; |     counters_need_update                 = false; | ||||||
|     debounce_counter_t *debounce_pointer = debounce_counters; |     debounce_counter_t *debounce_pointer = debounce_counters; | ||||||
|     for (uint8_t row = 0; row < num_rows; row++) { |     for (uint8_t row = 0; row < num_rows; row++) { | ||||||
|         for (uint8_t col = 0; col < MATRIX_COLS; col++) { |         for (uint8_t col = 0; col < MATRIX_COLS; col++) { | ||||||
|             if (*debounce_pointer != DEBOUNCE_ELAPSED) { |             if (*debounce_pointer != DEBOUNCE_ELAPSED) { | ||||||
|                 if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { |                 if (*debounce_pointer <= elapsed_time) { | ||||||
|                     *debounce_pointer = DEBOUNCE_ELAPSED; |                     *debounce_pointer = DEBOUNCE_ELAPSED; | ||||||
|                     cooked[row]       = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col)); |                     cooked[row]       = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col)); | ||||||
|                 } else { |                 } else { | ||||||
|  |                     *debounce_pointer -= elapsed_time; | ||||||
|                     counters_need_update = true; |                     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; |     debounce_counter_t *debounce_pointer = debounce_counters; | ||||||
|     for (uint8_t row = 0; row < num_rows; row++) { |     for (uint8_t row = 0; row < num_rows; row++) { | ||||||
|         matrix_row_t delta = raw[row] ^ cooked[row]; |         matrix_row_t delta = raw[row] ^ cooked[row]; | ||||||
|         for (uint8_t col = 0; col < MATRIX_COLS; col++) { |         for (uint8_t col = 0; col < MATRIX_COLS; col++) { | ||||||
|             if (delta & (ROW_SHIFTER << col)) { |             if (delta & (ROW_SHIFTER << col)) { | ||||||
|                 if (*debounce_pointer == DEBOUNCE_ELAPSED) { |                 if (*debounce_pointer == DEBOUNCE_ELAPSED) { | ||||||
|                     *debounce_pointer    = current_time; |                     *debounce_pointer    = DEBOUNCE; | ||||||
|                     counters_need_update = true; |                     counters_need_update = true; | ||||||
|                 } |                 } | ||||||
|             } else { |             } 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; } | bool debounce_active(void) { return true; } | ||||||
|  | #else | ||||||
|  | #    include "none.c" | ||||||
|  | #endif | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| /*
 | /*
 | ||||||
| Copyright 2017 Alex Ong<the.onga@gmail.com> | Copyright 2017 Alex Ong<the.onga@gmail.com> | ||||||
|  | Copyright 2021 Simon Arlott | ||||||
| This program is free software: you can redistribute it and/or modify | 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 | it under the terms of the GNU General Public License as published by | ||||||
| the Free Software Foundation, either version 2 of the License, or | 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 | #    define DEBOUNCE 5 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | // Maximum debounce: 255ms
 | ||||||
|  | #if DEBOUNCE > UINT8_MAX | ||||||
|  | #    undef DEBOUNCE | ||||||
|  | #    define DEBOUNCE UINT8_MAX | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #define ROW_SHIFTER ((matrix_row_t)1) | #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 debounce_counter_t *debounce_counters; | ||||||
|  | static fast_timer_t        last_time; | ||||||
| static bool                counters_need_update; | static bool                counters_need_update; | ||||||
| static bool                matrix_need_update; | static bool                matrix_need_update; | ||||||
| 
 | 
 | ||||||
| #define DEBOUNCE_ELAPSED 251 | #define DEBOUNCE_ELAPSED 0 | ||||||
| #define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) |  | ||||||
| 
 | 
 | ||||||
| static uint8_t wrapping_timer_read(void) { | static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); | ||||||
|     static uint16_t time        = 0; | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); | ||||||
|     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; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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
 | // we use num_rows rather than MATRIX_ROWS to support split keyboards
 | ||||||
| void debounce_init(uint8_t num_rows) { | 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) { | 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) { |     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) { |     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.
 | // 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; |     counters_need_update                 = false; | ||||||
|  |     matrix_need_update                   = false; | ||||||
|     debounce_counter_t *debounce_pointer = debounce_counters; |     debounce_counter_t *debounce_pointer = debounce_counters; | ||||||
|     for (uint8_t row = 0; row < num_rows; row++) { |     for (uint8_t row = 0; row < num_rows; row++) { | ||||||
|         for (uint8_t col = 0; col < MATRIX_COLS; col++) { |         for (uint8_t col = 0; col < MATRIX_COLS; col++) { | ||||||
|             if (*debounce_pointer != DEBOUNCE_ELAPSED) { |             if (*debounce_pointer != DEBOUNCE_ELAPSED) { | ||||||
|                 if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { |                 if (*debounce_pointer <= elapsed_time) { | ||||||
|                     *debounce_pointer = DEBOUNCE_ELAPSED; |                     *debounce_pointer = DEBOUNCE_ELAPSED; | ||||||
|  |                     matrix_need_update = true; | ||||||
|                 } else { |                 } else { | ||||||
|  |                     *debounce_pointer -= elapsed_time; | ||||||
|                     counters_need_update = true; |                     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;
 | // 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) { | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { | ||||||
|     matrix_need_update                   = false; |  | ||||||
|     debounce_counter_t *debounce_pointer = debounce_counters; |     debounce_counter_t *debounce_pointer = debounce_counters; | ||||||
|     for (uint8_t row = 0; row < num_rows; row++) { |     for (uint8_t row = 0; row < num_rows; row++) { | ||||||
|         matrix_row_t delta        = raw[row] ^ cooked[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); |             matrix_row_t col_mask = (ROW_SHIFTER << col); | ||||||
|             if (delta & col_mask) { |             if (delta & col_mask) { | ||||||
|                 if (*debounce_pointer == DEBOUNCE_ELAPSED) { |                 if (*debounce_pointer == DEBOUNCE_ELAPSED) { | ||||||
|                     *debounce_pointer    = current_time; |                     *debounce_pointer    = DEBOUNCE; | ||||||
|                     counters_need_update = true; |                     counters_need_update = true; | ||||||
|                     existing_row ^= col_mask;  // flip the bit.
 |                     existing_row ^= col_mask;  // flip the bit.
 | ||||||
|                 } else { |  | ||||||
|                     matrix_need_update = true; |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             debounce_pointer++; |             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; } | bool debounce_active(void) { return true; } | ||||||
|  | #else | ||||||
|  | #    include "none.c" | ||||||
|  | #endif | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| /*
 | /*
 | ||||||
| Copyright 2019 Alex Ong<the.onga@gmail.com> | Copyright 2019 Alex Ong<the.onga@gmail.com> | ||||||
|  | Copyright 2021 Simon Arlott | ||||||
| This program is free software: you can redistribute it and/or modify | 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 | it under the terms of the GNU General Public License as published by | ||||||
| the Free Software Foundation, either version 2 of the License, or | 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 | #    define DEBOUNCE 5 | ||||||
| #endif | #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 bool matrix_need_update; | ||||||
| 
 | 
 | ||||||
| static debounce_counter_t *debounce_counters; | static debounce_counter_t *debounce_counters; | ||||||
|  | static fast_timer_t        last_time; | ||||||
| static bool                counters_need_update; | static bool                counters_need_update; | ||||||
| 
 | 
 | ||||||
| #define DEBOUNCE_ELAPSED 251 | #define DEBOUNCE_ELAPSED 0 | ||||||
| #define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) |  | ||||||
| 
 | 
 | ||||||
| static uint8_t wrapping_timer_read(void) { | static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); | ||||||
|     static uint16_t time        = 0; | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); | ||||||
|     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; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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
 | // we use num_rows rather than MATRIX_ROWS to support split keyboards
 | ||||||
| void debounce_init(uint8_t num_rows) { | void debounce_init(uint8_t num_rows) { | ||||||
| @ -63,27 +62,50 @@ void debounce_init(uint8_t num_rows) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | void debounce_free(void) { | ||||||
|     uint8_t current_time  = wrapping_timer_read(); |     free(debounce_counters); | ||||||
|     bool    needed_update = counters_need_update; |     debounce_counters = NULL; | ||||||
|     if (counters_need_update) { |  | ||||||
|         update_debounce_counters(num_rows, current_time); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|     if (changed || (needed_update && !counters_need_update) || matrix_need_update) { | void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | ||||||
|         transfer_matrix_values(raw, cooked, num_rows, current_time); |     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(num_rows, elapsed_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.
 | // 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; |     counters_need_update                 = false; | ||||||
|  |     matrix_need_update                   = false; | ||||||
|     debounce_counter_t *debounce_pointer = debounce_counters; |     debounce_counter_t *debounce_pointer = debounce_counters; | ||||||
|     for (uint8_t row = 0; row < num_rows; row++) { |     for (uint8_t row = 0; row < num_rows; row++) { | ||||||
|         if (*debounce_pointer != DEBOUNCE_ELAPSED) { |         if (*debounce_pointer != DEBOUNCE_ELAPSED) { | ||||||
|             if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { |             if (*debounce_pointer <= elapsed_time) { | ||||||
|                 *debounce_pointer = DEBOUNCE_ELAPSED; |                 *debounce_pointer = DEBOUNCE_ELAPSED; | ||||||
|  |                 matrix_need_update = true; | ||||||
|             } else { |             } else { | ||||||
|  |                 *debounce_pointer -= elapsed_time; | ||||||
|                 counters_need_update = true; |                 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;
 | // 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) { | static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { | ||||||
|     matrix_need_update                   = false; |  | ||||||
|     debounce_counter_t *debounce_pointer = debounce_counters; |     debounce_counter_t *debounce_pointer = debounce_counters; | ||||||
|     for (uint8_t row = 0; row < num_rows; row++) { |     for (uint8_t row = 0; row < num_rows; row++) { | ||||||
|         matrix_row_t existing_row = cooked[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
 |         // determine new value basd on debounce pointer + raw value
 | ||||||
|         if (existing_row != raw_row) { |         if (existing_row != raw_row) { | ||||||
|             if (*debounce_pointer == DEBOUNCE_ELAPSED) { |             if (*debounce_pointer == DEBOUNCE_ELAPSED) { | ||||||
|                 *debounce_pointer    = current_time; |                 *debounce_pointer    = DEBOUNCE; | ||||||
|                 cooked[row]          = raw_row; |                 cooked[row]          = raw_row; | ||||||
|                 counters_need_update = true; |                 counters_need_update = true; | ||||||
|             } else { |  | ||||||
|                 matrix_need_update = true; |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         debounce_pointer++; |         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; } | bool debounce_active(void) { return true; } | ||||||
|  | #else | ||||||
|  | #    include "none.c" | ||||||
|  | #endif | ||||||
|  | |||||||
							
								
								
									
										229
									
								
								quantum/debounce/tests/debounce_test_common.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								quantum/debounce/tests/debounce_test_common.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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) { | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								quantum/debounce/tests/debounce_test_common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								quantum/debounce/tests/debounce_test_common.h
									
									
									
									
									
										Normal file
									
								
							| @ -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_; | ||||||
|  | }; | ||||||
							
								
								
									
										39
									
								
								quantum/debounce/tests/rules.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								quantum/debounce/tests/rules.mk
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | # 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 | ||||||
							
								
								
									
										223
									
								
								quantum/debounce/tests/sym_defer_g_tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								quantum/debounce/tests/sym_defer_g_tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||||
|  | } | ||||||
							
								
								
									
										225
									
								
								quantum/debounce/tests/sym_defer_pk_tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								quantum/debounce/tests/sym_defer_pk_tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||||
|  | } | ||||||
							
								
								
									
										237
									
								
								quantum/debounce/tests/sym_eager_pk_tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								quantum/debounce/tests/sym_eager_pk_tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||||
|  | } | ||||||
							
								
								
									
										280
									
								
								quantum/debounce/tests/sym_eager_pr_tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								quantum/debounce/tests/sym_eager_pr_tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								quantum/debounce/tests/testlist.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								quantum/debounce/tests/testlist.mk
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | TEST_LIST += \
 | ||||||
|  | 	debounce_sym_defer_g \
 | ||||||
|  | 	debounce_sym_defer_pk \
 | ||||||
|  | 	debounce_sym_eager_pk \
 | ||||||
|  | 	debounce_sym_eager_pr | ||||||
| @ -1,6 +1,7 @@ | |||||||
| TEST_LIST = $(notdir $(patsubst %/rules.mk,%,$(wildcard $(ROOT_DIR)/tests/*/rules.mk))) | TEST_LIST = $(notdir $(patsubst %/rules.mk,%,$(wildcard $(ROOT_DIR)/tests/*/rules.mk))) | ||||||
| FULL_TESTS := $(TEST_LIST) | FULL_TESTS := $(TEST_LIST) | ||||||
| 
 | 
 | ||||||
|  | include $(ROOT_DIR)/quantum/debounce/tests/testlist.mk | ||||||
| include $(ROOT_DIR)/quantum/sequencer/tests/testlist.mk | include $(ROOT_DIR)/quantum/sequencer/tests/testlist.mk | ||||||
| include $(ROOT_DIR)/quantum/serial_link/tests/testlist.mk | include $(ROOT_DIR)/quantum/serial_link/tests/testlist.mk | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								tmk_core/common/arm_atsam/_timer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tmk_core/common/arm_atsam/_timer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | /* 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/>.
 | ||||||
|  |  */ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | // The platform is 32-bit, so prefer 32-bit timers to avoid overflow
 | ||||||
|  | #define FAST_TIMER_T_SIZE 32 | ||||||
							
								
								
									
										19
									
								
								tmk_core/common/avr/_timer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tmk_core/common/avr/_timer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | /* 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/>.
 | ||||||
|  |  */ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | // The platform is 8-bit, so prefer 16-bit timers to reduce code size
 | ||||||
|  | #define FAST_TIMER_T_SIZE 16 | ||||||
							
								
								
									
										19
									
								
								tmk_core/common/chibios/_timer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tmk_core/common/chibios/_timer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | /* 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/>.
 | ||||||
|  |  */ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | // The platform is 32-bit, so prefer 32-bit timers to avoid overflow
 | ||||||
|  | #define FAST_TIMER_T_SIZE 32 | ||||||
| @ -1,5 +1,6 @@ | |||||||
| /*
 | /*
 | ||||||
| Copyright 2011 Jun Wako <wakojun@gmail.com> | Copyright 2011 Jun Wako <wakojun@gmail.com> | ||||||
|  | Copyright 2021 Simon Arlott | ||||||
| 
 | 
 | ||||||
| This program is free software: you can redistribute it and/or modify | 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 | it under the terms of the GNU General Public License as published by | ||||||
| @ -17,13 +18,13 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>. | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #if __has_include_next("_timer.h") | ||||||
|  | #    include_next "_timer.h" /* Include the platform's _timer.h */ | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
| #if defined(__AVR__) |  | ||||||
| #    include "avr/timer_avr.h" |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #define TIMER_DIFF(a, b, max) ((max == UINT8_MAX) ? ((uint8_t)((a) - (b))) : ((max == UINT16_MAX) ? ((uint16_t)((a) - (b))) : ((max == UINT32_MAX) ? ((uint32_t)((a) - (b))) : ((a) >= (b) ? (a) - (b) : (max) + 1 - (b) + (a))))) | #define TIMER_DIFF(a, b, max) ((max == UINT8_MAX) ? ((uint8_t)((a) - (b))) : ((max == UINT16_MAX) ? ((uint16_t)((a) - (b))) : ((max == UINT32_MAX) ? ((uint32_t)((a) - (b))) : ((a) >= (b) ? (a) - (b) : (max) + 1 - (b) + (a))))) | ||||||
| #define TIMER_DIFF_8(a, b) TIMER_DIFF(a, b, UINT8_MAX) | #define TIMER_DIFF_8(a, b) TIMER_DIFF(a, b, UINT8_MAX) | ||||||
| #define TIMER_DIFF_16(a, b) TIMER_DIFF(a, b, UINT16_MAX) | #define TIMER_DIFF_16(a, b) TIMER_DIFF(a, b, UINT16_MAX) | ||||||
| @ -47,6 +48,21 @@ uint32_t timer_elapsed32(uint32_t last); | |||||||
| #define timer_expired(current, future) ((uint16_t)(current - future) < UINT16_MAX / 2) | #define timer_expired(current, future) ((uint16_t)(current - future) < UINT16_MAX / 2) | ||||||
| #define timer_expired32(current, future) ((uint32_t)(current - future) < UINT32_MAX / 2) | #define timer_expired32(current, future) ((uint32_t)(current - future) < UINT32_MAX / 2) | ||||||
| 
 | 
 | ||||||
|  | // Use an appropriate timer integer size based on architecture (16-bit will overflow sooner)
 | ||||||
|  | #if FAST_TIMER_T_SIZE < 32 | ||||||
|  | #    define TIMER_DIFF_FAST(a, b) TIMER_DIFF_16(a, b) | ||||||
|  | #    define timer_expired_fast(current, future) timer_expired(current, future) | ||||||
|  | typedef uint16_t fast_timer_t; | ||||||
|  | fast_timer_t inline timer_read_fast(void) { return timer_read(); } | ||||||
|  | fast_timer_t inline timer_elapsed_fast(fast_timer_t last) { return timer_elapsed(last); } | ||||||
|  | #else | ||||||
|  | #    define TIMER_DIFF_FAST(a, b) TIMER_DIFF_32(a, b) | ||||||
|  | #    define timer_expired_fast(current, future) timer_expired32(current, future) | ||||||
|  | typedef uint32_t fast_timer_t; | ||||||
|  | fast_timer_t inline timer_read_fast(void) { return timer_read32(); } | ||||||
|  | fast_timer_t inline timer_elapsed_fast(fast_timer_t last) { return timer_elapsed32(last); } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Simon Arlott
						Simon Arlott