Auto-repeat keypad code with variable delays - Page 1
Forum section: electronics / general

author: ricko_uk
author_rank: Super Contributor
timestamp: December 20, 2019, 03:12:00 am
content: Hi,what is the best way to implement a auto-repeat keypad de-bouncing system/code so that it debounce and also the autorepeat rate changes depending on how long the user keeps the buttons pressed? Like the first 50ms is the debounce, then after half second it is considered the first click then after 2 seconds it starts autorepeat at a 10x rate and after 5 seconds it increases the autorepeat rate to 50x or more?Not just the general setup and functionality details but ideally also the data structure setup.Many thanks

author: Nominal Animal
author_rank: Super Contributor
timestamp: December 21, 2019, 02:48:10 pm
content: Quote from: ricko_uk on December 20, 2019, 03:12:00 amAs you have by now noticed, asking a question like that will be met with crickets.At minimum, you need to qualify "best". Better yet, state your constraints (microcontroller, number of buttons, memory available), instead of asking for "best".Quote from: ricko_uk on December 20, 2019, 03:12:00 amI like an approach that has no trigger delay, and allows you to define the autorepeat rates. I'll describe the scheme here.First, you have a state counter, say anuint16_tor auint32_t, for each button.You scan the button matrix at regular intervals. It does not need to be absolutely regular; it's just that the autorepeat effects occur as a function of the number of scans, instead of a timer. So, some jitter is perfectly acceptable.Code:/* Initial debounce 30 scan cycles */#define BUTTON_DEBOUNCE 30/* Button state high bit */#define BUTTON_HIGHBIT 32768/* Button states */static uint16_t button = {0};Code:/** Button scan loop.*/for (int i=0; i < BUTTONS; i++) {if (button_pressed(i)) {/* Button i is physically pressed */if (!button_state) {EMIT_BUTTON_PRESS(i);button_state = 1;} else {/* Increment autorepeat counter. At overflow, keep high bit set. */button_state = (button_state + 1) | (button_state & BUTTON_HIGHBIT);if (BUTTON_AUTOREPEAT(button_state)) {EMIT_BUTTON_AUTOREPEAT(i);}}} elseif (button_state) {/* Button i is released (was pressed, now not pressed) */if (button_state > BUTTON_DEBOUNCE) {/* Debounce duration is over. */EMIT_BUTTON_RELEASE(i);button_state = 0;} else {/* Within initial debounce; ignore. */button_state++;}}}FunctionsEMIT_BUTTON_PRESS(i)andEMIT_BUTTON_AUTOREPEAT(i)are called when buttonipress event should occur. FunctionEMIT_BUTTON_RELEASE(i)is called when buttonirelease event should occur.FunctionBUTTON_AUTOREPEAT(state)is a function that returns nonzero for all state counters when a new autorepeat press event should be emitted, and zero for all others. If you have 8192 bytes of Flash/ROM available for debouncing, you can do arbitrary repeat sequences usingCode:static const unsigned char button_autorepeat_bitmap = { /* Omitted for brevity */ };static inline int BUTTON_AUTOREPEAT(uint16_t i){return button_autorepeat_bitmap[i / 8] & (1 << (i & 7));}Another option is a sequence of if clauses, sayCode:#define BUTTON_INTERVAL1 25#define BUTTON_INTERVAL2 10#define BUTTON_INTERVAL3 5static inline int BUTTON_AUTOREPEAT(uint16_t i){if (i < 5 * BUTTON_DEBOUNCE) {return ((i % BUTTON_DEBOUNCE) == 0);} else {i -= 5 * BUTTON_DEBOUNCE;}if (i < 10 * BUTTON_INTERVAL1) {return ((i % BUTTON_INTERVAL1) == 0);} else {i -= 10 * BUTTON_INTERVAL1;}if (i < 10 * BUTTON_INTERVAL2) {return ((i % BUTTON_INTERVAL2) == 0);} else {i -= 10 * BUTTON_INTERVAL2;}return ((i % BUTTON_INTERVAL3) == 0);}You can also use some of the bits in the counter to record whether a button press event is pending or not, so that you can limit how many events you need to process per scan, to keep the scanning loop duration to minimum (if you need to do other stuff at smaller intervals). Or you can use a queue of button events. If I knew more of your solution constraints, I could give much better suggestions.Do consider the above as astarting point, not a final solution, as I'm trying to keep as many options open as possible with it; it is an initial outline suggestion for one possible solution.Unlike other debouncing schemes, this has the property of triggering a press event immediately when the button is pressed; state changes will simply be ignored for the nextBUTTON_DEBOUNCE-1scan iterations.

author: madires
author_rank: Super Contributor
timestamp: December 21, 2019, 03:40:45 pm
content: As Nominal Animal suggested, track the time ticks (time intervals) the button is pressed and set flags or a variable accordingly. If you don't have to deal with button combinations pressed at the same time you can simplify things by using a single counter for the time ticks, a single flag/variable for the repeat status and a variable for the previous button ID. If the button scan results in the same button still being pressed, increase the tick counter, check for thresholds and set flags. If not, reset flags and variables. Based on what kind of settings/values you want to change a dynamic scheme (as longer you press the larger the change) instead of the fixed steps might be more user friendly.