As a kid, I got the book “Upgrading and Repairing PCs.” (Now in its 22nd edition.) It was the first book to explain to me the PC architecture. I considered, how were there so few pins on an AT-style keyboard connector when there were 101 keys on the keyboard? That is when I first learned about the keyboard matrix.
Intel_P8049_AH_controller
Original image from Deskthority Wiki. (Edited image is shown.)
The keyboard matrix itself did not amaze me, but instead the idea there was an entirely separate 8-bit microcontroller inside of the keyboard. Early keyboards may have used the P8049AH, which, there is still some stock available to purchase. I was fascinated with the idea an entire computer was necessary to run the keyboard, to use my “real” computer. Why did it take something as complicated as a microcontroller? The key benefit (get it?) of a keyboard matrix is that it reduces the number of pins necessary to capture the input of a large number of the keys. Even though a PC keyboard has 101 keys, it does not mean there is a microcontroller with 101 pins. Nor does it need a cable with over 100 wires. I will first explain with simple four and nine button examples.

Without a matrix

First, let’s look at what happens with four buttons. Without an array, each switch would get an input pin. This count probably does not sound bad. Now, what if you used nine buttons instead of just 4? 9 buttons without a matrix You would need 9 I/O pins! Also, consider the cost of wiring that many individual buttons. If the buttons are on a different PCB from the microcontroller or you are hand wiring a prototype, that is many wires. Let’s put those same nine buttons into a 3×3 matrix. This method saves you three pins! 9x9 Keyboard Matrix

Elements of a Keyboard Matrix

Every matrix has rows and columns. By accessing a single row and a single column, we can individually access each button. This method drives one side and senses the other.

Keyboard Matrix Diodes

Ghosting Example Keyboard Matrix
Matrix with and without blocking diodes
In the schematic, I have included blocking diodes. The diode prevents a condition called “ghosting.” In a keyboard matrix, ghosting means you see non-existent button pushes. The image above compares the same button presses with and without diodes. (The 1 ohm resistors keep iCircuit from getting annoyed with short circuits.) On the right-side schematic, reading the “selected” button happens with no additional current paths.

Keyboard Matrix Code

For() loops and arrays make the code work. The steps for scanning the keyboard matrix include:
  1. Enable the column
  2. Scanning each row
  3. Capture button state
  4. Disable the column

// Keyboard Matrix Tutorial Example
// baldengineer.com
// CC BY-SA 4.0
 
// JP1 is an input
byte rows[] = {2,3,4};
const int rowCount = sizeof(rows)/sizeof(rows[0]);

// JP2 and JP3 are outputs
byte cols[] = {8,9,10};
const int colCount = sizeof(cols)/sizeof(cols[0]);

byte keys[colCount][rowCount];

void setup() {
	Serial.begin(115200);

	for(int x=0; x<rowCount; x++) {
		Serial.print(rows[x]); Serial.println(" as input");
		pinMode(rows[x], INPUT);
	}

	for (int x=0; x<colCount; x++) {
		Serial.print(cols[x]); Serial.println(" as input-pullup");
		pinMode(cols[x], INPUT_PULLUP);
	}
		
}

void readMatrix() {
	// iterate the columns
	for (int colIndex=0; colIndex < colCount; colIndex++) {
		// col: set to output to low
		byte curCol = cols[colIndex];
		pinMode(curCol, OUTPUT);
		digitalWrite(curCol, LOW);

		// row: interate through the rows
		for (int rowIndex=0; rowIndex < rowCount; rowIndex++) {
			byte rowCol = rows[rowIndex];
			pinMode(rowCol, INPUT_PULLUP);
			keys[colIndex][rowIndex] = digitalRead(rowCol);
			pinMode(rowCol, INPUT);
		}
		// disable the column
		pinMode(curCol, INPUT);
	}
}

void printMatrix() {
	for (int rowIndex=0; rowIndex < rowCount; rowIndex++) {
		if (rowIndex < 10)
			Serial.print(F("0"));
		Serial.print(rowIndex); Serial.print(F(": "));

		for (int colIndex=0; colIndex < colCount; colIndex++) {	
			Serial.print(keys[colIndex][rowIndex]);
			if (colIndex < colCount)
				Serial.print(F(", "));
		}	
		Serial.println("");
	}
	Serial.println("");
}

void loop() {
	readMatrix();
	if (Serial.read()=='!')
		printMatrix();
}

1. Column Enable (Line 32)

Keyboard matrix columns are enabled by setting the pin to OUTPUT and then to LOW. This step provides the path to ground. The rest of the columns pins are held in their high impedance state, effectively disabling them from the matrix.

2. Scan each row (Line 39)

A for() loop runs through each pin the row array. The pin’s input pull-up resistor is enabled, providing the connection to VCC. Most Arduino boards turn on the resistor with pinMode()’s INPUT_PULLUP state.

3. Capture the pin’s state

A two-dimensional array stores the pin’s value. The pin’s pull-up resistor is turned off, and the loop increments. The idea here is to capture all of the pressed buttons. After the matrix has been scanned action can be taken. Just like with regular buttons, you could also create a “previous state” matrix to detect button state changes. Once read, the pin’s state goes back to INPUT, disabling the row by turning off the pull-up resistor.

Bitwise operators would be better

I’d like to point out; this method is very memory inefficient. The problem is that an entire byte, or 8 bits, is being used to store the state of each button. You only need 1 bit for each. A more efficient method would be using bitwise operators to keep track of each key as a bit.

4. Disable the Column

After checking each row, putting the column pin back to an INPUT state disables it. Processing the newly acquired button presses happens after scanning the entire matrix. In this example code, the function “printMatrix()” prints the contents of the array. In your sketch, you would perform some action based on the button’s states.

Potential Optimizations

As mentioned before, you could be using bit operators to store each button press as a single bit, instead of a byte. Converting the code into a state machine using millis() could be critical on slower MCUs or very time sensitive code. As it is, most matrices will be scanned so fast the blocking time doesn’t matter. However, I could imagine using a microcontroller at 1 MHz where the time it takes for a large matrix read to finish might be too much.

Alternative code

There is an Arduino keyboard matrix library available. You can install Keypad from the Library manager. Arduino Keypad Library It simplifies programming a matrix. The most effort is defining the keys. A trade-off with the stock library is that it does not handle multiple key presses. There’s a code example for that, but that adds to the complexity.

Conclusion

A keyboard matrix is a great way to add buttons without using up all of your I/O pins. In this keyboard matrix tutorial,  I showed how a 9-button matrix works. This same code and circuit are what I’m using for a new project. My latest project has 64 buttons. More on that to follow.
Author

Fan of making things beep, blink and fly. Created AddOhms. Stream on Twitch. Video Host on element14 Presents and writing for Hackster.IO. Call sign KN6FGY.

16 Comments

  1. This is the guide I was looking for to understand how the keyboard matrix works. Thanks ;))

  2. Pingback: Keyboard Switch Report – phil caridi

  3. I assume that the upper button in the middle column should be considered to be open. Otherwise row 1 would be active in both figures and reporting (1,2) as active would be correct. This might also solve your problem with iCircuit.

  4. Thanks for this – made it easy to trace how a single button push flows.
    Still have one question, as to how to detect multiple buttons being pushed.
    If I number the 3×3 buttons as:
    123
    456
    789
    How do I know if it is 1, 2 and 4 being pushed, versus 1, 2 and 5? Wouldn’t either combination trigger col1 and col2, and row1 and row2?
    Thanks.

  5. Michael Bast Reply

    I have no idea if you’ll reply, but I’m trying to convert an old pipe organ console to MIDI. Long story short, I need to use multiplexers and create a matrix for the keyboard, but I’m unsure what sort of multiplexers I need, or how many. I want to achieve 16+ note polyphony, across a 61 note keyboard.

    • Sorry, not an area I have a lot of experience. You might try posting on something like the Arduino.cc Project Guidendance forum.

    • Michael Pardini Reply

      Not an expert on electronic musical keyboards, couldn’t this approach (matrix + multiplexing) be potentially incompatible with the time responsiveness required in music or potentially limit the polyphony your keyboard could transmit?

    • Using the attached library I was able to use an Arduino mega to midify a 2 manual organ with a full pedal board and 30+ stops. I’m currently working on building a 3rd manual and adding illuminated LED push buttons to replace the stops. The Arduino plugs into the computer and can be programmed to show up as a midi device.
      https://tttapa.github.io/Control-Surface-doc/Doxygen/index.html

  6. Hello,
    I tried that with 5×5 and the code … SoMeHoW work…
    When I push middle button output is
    11111
    11111
    00100
    11111
    11111
    Is there something with inversion?
    Or am I missing something?

    • The code should work fine for a 5×5, if modified correctly. The pattern suggests to me you did not wire it the same.

  7. Thanks for the code and the logic. I picked up some used 4X4 keypads at a sale (8 pinout) and with a vom figured the columns and rows and ground. Using the matrix library functions
    worked fine on my uno, but did not work on my mini or nano arduinos. Do you know if
    there is a special library for those versions? Also, can you recommend any ic that would simplify interfacing the keypad to the arduino without the pin overhead that the above methods use? I am thinking a SN74151 (1 of 8 data selector … I have crates of em’) would cut the IO pins to four, but the logic to make it work would be pretty sticky.

    • but did not work on my mini or nano arduinos.

      Not sure what you mean by a mini, but a Nano is the same 328p as the Uno. So there must be a wiring issue, because there is no difference in the code. It is the same processor silicon.

      can you recommend any ic that would simplify interfacing the keypad to the arduino without the pin overhead that the above methods use? I am thinking a SN74151

      On something small like a 4×4, I don’t see how a multiplexer and shift register is going to help. You burn up as many pins to control those chips as you would to control the matrix directly.

      • Is it possible to have two separate button matrixes. I have two 28 buttons in a 4×7 grid for a multiaxis joystick and gamepad using a joystick library by Matthew Heironimus

Write A Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.