Skip to content
Home » Embedded Systems » Microcontrollers » Matrix Keypad with Atmega8 – part 2

Matrix Keypad with Atmega8 – part 2

Compact 4x4 matrix keypad scanning diagram showing column C1 driven LOW while C2-C4 remain HIGH, with all 16 switches in open position demonstrating the column scanning technique for key detection

KEY TAKEAWAYS

  • This part extends the keypad implementation with practical code for detecting pressed keys
  • Scanning algorithm: activate one row at a time, read all columns, map the result to the key value
  • The scan rate must be fast enough to catch short key presses but slow enough to debounce reliably
  • Matrix keypads are common in embedded systems for user input: security panels, industrial controllers, and calculators

In the previous post – Matrix Keypad with Atmega8 – part 1, we discussed why a matrix keypad is useful and how we can use and interface it with an ATmega8. We will move on with out discussions.

In the last part – Matrix Keypad with Atmega8 – part 1 we discussed the need of a matrix keypad and how it works. Lets take the discussions forward. Here we will see why we need to make the columns HIGH Impedance and I will also share the code with you.

Why make other Columns High Impedance while one column is made LOW?

The way we have designed our solution is that we are making one column LOW while others are in TRISTATE. This gives rise to a common question – Why? Why the other columns in TRISTATE? Why not HIGH?

The questions are very valid and lets examine.

Lets say we selected column number C1, so we make it LOW(i.e. GND or logic 0) and at the same time we make all other columns high impedance (i.e. input).

If we don’t make other lines high impedance (tristate or Input) they are in output mode. And in output mode they must be either LOW(GND or logic 0) or HIGH (5v or logic 1). We can’t make other lines LOW as we can select only one line at a time and C1 is already low in this assumption. So the only other possible state is all other columns are HIGH. This is shown in figure below. Red colour on column indicate high state while green is for low state.

Now lets see what happen when a user presses two keys at once – say keys 1 and 2

Do you see what happens? It leads to a short circuit from the HIGH C2 to HIGH C1 which will burn the buffer of the MCU immediately.

Hence, to prevent it from happening, all other columns are kept at tristate(neither LOW nor HIGH) but very high input impedance that prevent either source or sink of current from them. So if we kept C2 at high impedance state it wont allow current to flow to GND on C1.

 

To help you out, a code file is placed here: Matrix_keypad.c

The code displays which key is pressed on an LCD. You will have to use the LCD libraries as well. You can download the libraries from here.

In the code, the connections are also explained. I suggest you try to understand the connections. In case you are not able to understand or have any doubts, do let me know in the comments below. Will be happy to help.

Making It Reliable: Debouncing and Multi-Key Handling

The basic scanning algorithm from Part 1 works for detecting single key presses. For a production-quality keypad interface, you need two additional features: debouncing and proper handling of simultaneous presses.

Software Debounce with State Machine

#include <avr/io.h>
#include <util/delay.h>

#define DEBOUNCE_MS    20
#define NO_KEY         0xFF

static uint8_t last_key = NO_KEY;
static uint8_t debounce_count = 0;

uint8_t scan_keypad(void);  /* Returns raw key or NO_KEY */

/* Call this every 5 ms from a timer ISR or main loop */
uint8_t get_debounced_key(void)
{
    uint8_t raw = scan_keypad();

    if (raw == last_key) {
        if (debounce_count < (DEBOUNCE_MS / 5)) {
            debounce_count++;
        }
    } else {
        last_key = raw;
        debounce_count = 0;
    }

    /* Key is stable for DEBOUNCE_MS — return it once */
    if (debounce_count == (DEBOUNCE_MS / 5) && raw != NO_KEY) {
        debounce_count++;  /* Prevent re-triggering */
        return raw;
    }

    return NO_KEY;
}

This state-machine approach avoids blocking delays. The function is called periodically (every 5 ms) and only returns a key when the same value has been read consistently for 20 ms. This is more robust than a simple _delay_ms() because it does not block the main loop.

Leave a Reply

Your email address will not be published. Required fields are marked *