Author Topic: Auto-repeat keypad code with variable delays  (Read 1489 times)

0 Members and 1 Guest are viewing this topic.

Offline ricko_ukTopic starter

  • Super Contributor
  • ***
  • Posts: 1015
  • Country: gb
Auto-repeat keypad code with variable delays
« on: December 20, 2019, 03:12:00 am »
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 :)
« Last Edit: December 20, 2019, 03:41:36 am by ricko_uk »
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6239
  • Country: fi
    • My home page and email address
Re: Auto-repeat keypad code with variable delays
« Reply #1 on: December 21, 2019, 02:48:10 pm »
what is the best way to [...]
As 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".

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?
I 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 an uint16_t or a uint32_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: [Select]
/* Initial debounce 30 scan cycles */
#define  BUTTON_DEBOUNCE  30

/* Button state high bit */
#define  BUTTON_HIGHBIT  32768

/* Button states */
static uint16_t  button[BUTTONS] = {0};
Code: [Select]
    /*
     * Button scan loop.
    */
    for (int i=0; i < BUTTONS; i++) {
        if (button_pressed(i)) {
            /* Button i is physically pressed */
            if (!button_state[i]) {
                EMIT_BUTTON_PRESS(i);
                button_state[i] = 1;
            } else {
                /* Increment autorepeat counter.  At overflow, keep high bit set. */
                button_state[i] = (button_state[i] + 1) | (button_state[i] & BUTTON_HIGHBIT);
                if (BUTTON_AUTOREPEAT(button_state[i])) {
                    EMIT_BUTTON_AUTOREPEAT(i);
                }
            }
        } else
        if (button_state[i]) {
            /* Button i is released (was pressed, now not pressed) */
            if (button_state[i] > BUTTON_DEBOUNCE) {
                /* Debounce duration is over. */
                EMIT_BUTTON_RELEASE(i);
                button_state[i] = 0;
            } else {
                /* Within initial debounce; ignore. */
                button_state[i]++;
            }
        }
    }
Functions EMIT_BUTTON_PRESS(i) and EMIT_BUTTON_AUTOREPEAT(i) are called when button i press event should occur.  Function EMIT_BUTTON_RELEASE(i) is called when button i release event should occur.

Function BUTTON_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 using
Code: [Select]
static const unsigned char   button_autorepeat_bitmap[8192] = {  /* 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, say
Code: [Select]
#define  BUTTON_INTERVAL1  25
#define  BUTTON_INTERVAL2  10
#define  BUTTON_INTERVAL3  5

static 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 a starting 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 next BUTTON_DEBOUNCE-1 scan iterations.
 
The following users thanked this post: ricko_uk

Offline madires

  • Super Contributor
  • ***
  • Posts: 7754
  • Country: de
  • A qualified hobbyist ;)
Re: Auto-repeat keypad code with variable delays
« Reply #2 on: December 21, 2019, 03:40:45 pm »
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.
 
The following users thanked this post: ricko_uk


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf