Read a Matrix Keypad with PIC16F877A in PICSimLab

A matrix keypad teaches row-and-column scanning: drive one row at a time, read the columns, and work out which key is pressed. PICSimLab provides a Keypad spare part, so a PIC16F877A project can practise the full scan loop in simulation, with no wiring mistakes possible.

What you need

  • PICSimLab 0.9.3 — see the download page.
  • MPLAB X IDE and the XC8 compiler.
  • Board: McLab2 or PICGenios, microcontroller PIC16F877A.
  • One Keypad spare part (Inputs category) and one LEDs spare part to show the pressed key.

Wiring used in this tutorial (example)

  • Keypad rows R1–R4 → RB0–RB3 (outputs, driven by the firmware)
  • Keypad columns C1–C4 → RB4–RB7 (inputs, using the PIC16F877A’s internal PORTB pull-ups)
  • LEDs spare part → PORTD, showing the value of the last key pressed

Set exactly this mapping in each part’s Properties window (right-click the part → Properties) after adding it.

Steps

1. Generate the project

Select the board and the PIC16F877A, then create the MPLAB X project with the Code Project Wizard, or start from a board example under Help → Examples.

2. Add the spare parts

Open Modules → Spare parts. Add Inputs → Keypad and Outputs → LEDs, then open each part’s Properties and connect the pins as listed above.

3. Understand the scan

The firmware drives one row low at a time and leaves the other rows high. If a key in that row is pressed, its column line is pulled low. Reading the columns for each row in turn identifies a single key. The PORTB weak pull-ups (enabled through OPTION_REGbits.nRBPU = 0) keep unpressed columns high — the PIC16F877A enables PORTB pull-ups as a group, which is all a keypad needs.

4. Build, load and run

Build the example in MPLAB X, load the .hex into PICSimLab and start the simulation. Click keys on the keypad part: the LEDs on PORTD light up with the key’s value (1–9, then 0x0A–0x0F for the bottom row and right column on a typical 4×4 layout; use the exact key labels printed on the part as your reference).

Example code (XC8)

// Example: 4x4 matrix keypad scan - PIC16F877A, XC8
// Rows RB0-RB3 (outputs), Columns RB4-RB7 (inputs, pull-ups on)
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CP = OFF

#include <xc.h>
#define _XTAL_FREQ 20000000     // Example: match the board clock

// Key map for the example wiring: row-major, rows RB0..RB3
static const unsigned char keymap[4][4] = {
    {1, 2, 3, 0x0A},
    {4, 5, 6, 0x0B},
    {7, 8, 9, 0x0C},
    {0x0E, 0, 0x0F, 0x0D}
};

// Returns the key value, or 0xFF when no key is pressed
unsigned char keypad_scan(void) {
    for (unsigned char row = 0; row < 4; row++) {
        PORTB = (unsigned char)(~(1u << row) & 0x0F) | (PORTB & 0xF0);
        __delay_us(50);                       // let the lines settle
        unsigned char cols = (PORTB >> 4) & 0x0F;
        if (cols != 0x0F) {                   // one column pulled low
            for (unsigned char col = 0; col < 4; col++) {
                if (!(cols & (1u << col))) return keymap[row][col];
            }
        }
    }
    return 0xFF;
}

void main(void) {
    TRISB = 0xF0;               // RB0-RB3 outputs, RB4-RB7 inputs
    TRISD = 0x00;               // PORTD outputs (LEDs)
    OPTION_REGbits.nRBPU = 0;   // enable PORTB pull-ups
    PORTD = 0x00;
    while (1) {
        unsigned char key = keypad_scan();
        if (key != 0xFF) {
            PORTD = key;        // show the key value on the LEDs
            __delay_ms(200);    // simple debounce / hold time
        }
    }
}

Try next

Troubleshooting

Every key reads as pressed, or no key ever reads. Check the direction split: rows must be outputs (TRISB low nibble 0) and columns inputs (high nibble 1), and the spare part rows and columns must not be swapped in Properties.

One key reports several values. You are probably reading the columns before the row line settles, or scanning while a previous key is still held. Keep the small settle delay and the hold time after a detected press.

Similar Posts