Matrix keyboard setup

In many cases you want to add some input interface to the project, the most simple is to use some push-buttons but what if you need to set some values, some of you might say:  you can do it with up-down, left-right, select button and some clever menu. Yes that’s about right but just imagine the software if you want to set values from 0-9999, or applications like access control, counters.

The most commonly known is the 12 (34) type which has off course 12 buttons noted from 0-9 and *, # for extra features. Well so far there isn’t anything clever about it, 12 buttons which need 12 input pins, wrong it needs just 7, the push buttons are organized in rows and columns, each row shares the same horizontal connection, while each column shares the same vertical connection. For a better understanding look at the schematic:

matrix keyboard schematic

Because of this matrix connection the reading of active button(which is pushed) isn’t so straightforward, each column has to be processed sequentially.

The resistors are external components, there are not included into the keypad but certainly needed, R1-R4 are the pull-up resistor, in the demo application the atmega88’s internal pull-ups are used, R5-R7 are for current limiting.

A few words about the firmware, the keyboard is connected to PORTB, PB0-PB3 are configured as inputs with the internal pull-up active, these will read which row is active, PB4-PB6 are configured as outputs and serve for column selection, their default value is logic ‘1’ the same as the row pins default value. The reading is made sequentially, the first column is selected by setting PB4(COL1) to logic ‘0’ and the pins PB0-PB3 are red, if no key is pressed the pins remain in their default state: logic ‘1’ else their value is logic’0′ we must go trough these steps for each column, once a pressed key is found its space thus value is determined, the function restores the corresponding column select pins default state and exits, this way speeding up the process.

Lets take an example: key 4 is pressed, column 1 is selected by setting PB4 to logic ‘0’, when we read PB0-PB3 PB1 will be logic ‘0’, since we know which column is active (it would be really funny otherwise since we wrote the firmware) the exact location of the pressed key is determined. Now we have 2 coordinates x(0..2),y(0..3)  x=1 is the row, y=0 is the column the most simple way to turn these coordinates into a value is using a 2 dimension look-up table:

const unsigned char KeyMap[3][4] = {{1,4,7,ESC},{2,5,8,0},{3,6,9,ENTER}}; // values ESC and ENTER are defined by macros

By simply addressing the table with the coordinates we have the corresponding value of the pressed key  value = KeyMap[x][y]; the look-up table can also hold any data of choice like ASCII, or can have other type definition as long as its dimension matches the keypads dimension [x][y].

Since the keypad or any other human machine interface tends to be asynchronous, which means that at any moment the firmware should be able to sense and to handle the input, the entire process or in other words the scanning must be executed periodically. This can be done inside some timer interrupt routine or just by adding some delays to the main cycle, if possible avoid using the mains power frequency or any multiple of this for scanning.

Also between the column selection and the effective pin reading some delay must be inserted, this allows time for the electrical state of the wires, traces to stabilize. After one pressed key is sensed the function exits, thus multiple pressed key will not be recorded although the modification needed to the firmware is quite simple, so consider this a challenge.

I attached the entire AVR Studio project, it is written in C, using the winavr package, it will be easy to integrate into any project. In the near future I will explain how to save I/O pins by sharing the row pins with other functions like driving the 7 segment display.

Matrix keyboard explained: [download]