๐Ÿ“ Pico Series
Episode 04 ยท Pico Learning Series
// CONCEPT: MATRIX SCANNING

KeypadMatrix Scanning

4ร—4 Keypad ยท GPIO Rows/Cols ยท Pull-up ยท Debounce
// What you'll learn
Why keypads use a matrix (saves GPIO pins)
How column-by-column scanning identifies a keypress
Pull-up resistors โ€” why floating inputs cause ghost presses
Software debouncing to prevent multiple triggers
// Click keys โ€” live matrix scanner
// PuTTY Serial Console
Waiting for keypress...
โ€”
Active Col (LOW)
โ€”
Detected Row
// 01 โ€” Core Concepts

The Matrix Trick

๐Ÿ”ข
Why a Matrix?
A 4ร—4 keypad has 16 keys. Wiring each key directly would need 16 GPIO pins. A matrix only needs 4 rows + 4 cols = 8 pins for 16 keys. For a 100-key keyboard, that's 10+10 = 20 pins vs 100. The trade-off: software must scan.
8 pins = 16 keys
๐Ÿ”
Column Scanning
One column at a time is pulled LOW. All other columns stay HIGH. Then all 4 row pins are read. If a row reads LOW, a key in that column AND row is pressed โ€” the intersection identifies the exact key.
gpio_put(col, 0)
โฌ†๏ธ
Pull-up Resistors
Without a pull-up, an unconnected GPIO floats โ€” it can read random 0s and 1s, causing ghost keypresses. The Pico's internal pull-ups hold row pins at HIGH (1) by default. A key press shorts the row to the LOW column.
gpio_pull_up()
// 02 โ€” Scanning Algorithm

How Column Scan Works

// Column 0 Active โ†’ Row Scan
C0
C1
C2
C3
Active col โ†’ LOW (0)
Other cols โ†’ HIGH (1)
Read all rows โ†’ if row == LOW โ†’ key found!
keypad.c
#define NUM_ROWS 4
#define NUM_COLS 4

const uint8_t rowPins[4] = {10,11,12,13};
const uint8_t colPins[4] = {18,19,20,21};

char keyMap[NUM_ROWS][NUM_COLS] = {
    {'1','2','3','A'},
    {'4','5','6','B'},
    {'7','8','9','C'},
    {'*','0','#','D'}
};

void initializeKeypad() {
    // Columns: OUTPUT, start HIGH
    for (int i=0; i<NUM_COLS; i++) {
        gpio_init(colPins[i]);
        gpio_set_dir(colPins[i], GPIO_OUT);
        gpio_put(colPins[i], 1);
    }
    // Rows: INPUT with pull-up
    for (int i=0; i<NUM_ROWS; i++) {
        gpio_init(rowPins[i]);
        gpio_set_dir(rowPins[i], GPIO_IN);
        gpio_pull_up(rowPins[i]);
    }
}

char readKeypad() {
    for (int col=0; col<NUM_COLS; col++) {
        gpio_put(colPins[col], 0); // activate col
        for (int row=0; row<NUM_ROWS; row++) {
            if (!gpio_get(rowPins[row])) {
                // Key pressed at [row][col]!
                while(!gpio_get(rowPins[row])){} // wait release
                gpio_put(colPins[col], 1);
                return keyMap[row][col];
            }
        }
        gpio_put(colPins[col], 1); // deactivate col
    }
    return 0;
}

int main() {
    stdio_init_all();
    initializeKeypad();
    while(1) {
        char key = readKeypad();
        if (key != 0) {
            printf("Key pressed: %c\n", key);
            sleep_ms(200); // debounce
        }
    }
}