This isn't a good approach; focusing on a narrow section (the keypad itself) limits you from seeing higher-level solutions.
The better and more traditional overall approach, is to run the keypad as its own task. Either get a peripheral that does scan and debounce itself (then I2C or SPI to read once in a while), or run a timer interrupt to do the scan and read on the MCU.
Doing it on the MCU, you need to integrate your own debounce, of course. A typical system might do:
- Set a given column as active, clear the others (use a diode type switch matrix).
- Wait a modest delay: at least the propagation delay of the GPIOs (for AVR, a few CPU cycles; trivial in C, but worth being aware of in ASM), and any additional delay in case analog filtering has been applied.
- Read the row bits (use external, or enable weak-pull-up resistors) and sore in an array.
- Repeat for a full scan (every low-100s Hz; a ~kHz heartbeat timer is typical).
- To implement debounce, have an array of bytes (or packed nibbles if you're short on RAM), one for each key; for each scan that returns a '1', increment the counter up to some maximum/limiting value; for each '0', decrement until zero. When the count equals max, set the output bit; when zero, clear the output bit.
Thus an indeterminate and noisy input does not change the detected value (or takes much longer to change), and the minimum delay for an input is scan rate / max count.
For press (edge) detection, compare previous to current value of the respective bit in the output array (you might want to keep a copy of previous inputs to enable this). For press-and-hold, make a counter, similar to the debounce timer, but the count is reset immediately on release, and it either triggers once on passing the desired count (and continues counting up to some irrelevant max, or perhaps looping on reaching some further count to make a press-and-repeat function).
The program in general should be an infinite loop that performs a sequence of actions, then waits for next heartbeat (which can then run a CPU sleep()
to save power, if that's relevant); a discrete-time sampled state machine.
This may or may not be readily adaptable to the rest of your program as-is, but it's a good archetype in general for interactive programs; understand the concept of threading, what a thread is, how to sequence and communicate between them, etc. (namely: an interrupt is probably the most basic example of a thread). Maybe you run the matrix scan process in its own thread, and synchronize a copy of the outputs into the main thread, and maybe that's "thread" as in part of the timer interrupt, maybe it's another task in main()
itself, whatever.