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.
/* 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};
/*
* 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
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
#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.