The circuit here described is a simple 4×5 keypad that can be used in particular with the PIC 16F877 microcontroller, for which it has been designed considering a supply of 5V, 16F877 I/O pins leakage current, the voltage level recognized as a high or low state while in TTL or ST mode, etc.. Since this circuit is quite simple, and the resistors’ value not critical, it should be suitable for other PICs and other microcontrollers, too. This keypad can also be used with logic and analogue crcuits; in such a case, it could be necessary to modify resistors’ value. We will refer, in this page, to the use with a PIC 16F877. The keys used are normally open, with four pins (see image below); when not pressed, only pins on the same side are connected together (left or right side in the image below); when the key is pressed, the four pins are connected all together.
A characteristic of this circuit is the possibility of scaling: it can in fact be realized with 1 to 4 rows and 1 to 5 columns (if you still have free rows or columns, you can add later other keys and other resistors); you can build, as an example, a 12 keys telephonic keypad, a simple 4 keys keypad for the 4 directions, a keypad to play tris 🙂 … in one word, what you like.
The figure below shows how it works (classic).
Only the positive supply is connected to the circuit (connector JP1); each row of the matrix keyboard is connected to Vdd by its own pull-up (R1 to R4), and, through a protection resistor (R5 to R8), to the microcontroller I/O pin used to read the state of that row (connector JP2). Columns are driven by PIC (connector JP3), still through 220 Ohm protection resistors (R9 to R13).
Driving columns and reading rows’ state
Referring to this keypad as an interface to a microcontroller (not necessarily a 16F877), the reading of keys’ status is related to the matrix structure. As previously said, each key (or switch) has four pins, connected two by two; in the top view of the circuit (above here), the upper pin on the left is connected to a row, the upper one on the right to a column. The lower pin on the left is not used, the lower one on the right simply carries the column signal to the key of the next row. Let’s consider a certain row: when no key connected to it is being pressed, all the row pins of that row are kept in the high state by the row pull-up; when a key is pressed, the column pin is connected to the row pin; if, in that moment, that column is in high impedance, it is driven at the row state; if it is instead driven low by the microcontroller, also the row is driven at the column state (low state). The microcontroller’s ports connected to the rows must always be set as inputs, or, in general, must be kept in high impedance; in general, also the columns must be in high impedance. To read the state of the keys of a column, it is needed to drive low that column (only one) and to wait a few milliseconds both for the row’s signal settling and to avoid the key’s rebound; at this moment the column’s state can be read on ports connected to rows. A suitable waiting time is five-ten milliseconds. As it is known which is the driven column, reading the rows’ state it is possible to know the keys’ status on that column. At this moment the pin driving that column must be put again in a high impedance state, and, possibly after a few milliseconds, a new column can be driven low and so on. Of course, it is not necessary to waist all this time in waiting: when waiting for settling, and in general among different readings, the microcontroller can execute (thousand of) other instructions. A typical way of reading keys’ status could be using interrupts. As an example, in the PICs the timer could be set to generate an interrupt every a few tens milliseconds. A variable coding the action to take (action), and a variable keeping track of the column to read (coltoread) could be used. The first time that the interrupt routine is executed, it sees those two variables at zero (they should have been set to zero, of course): in such a case, the action to take is to drive low the column 0 and to increment the variable coding the action to take. When the next interrupt occurs, the interrupt driver sees such a variable at 1, so reads the rows’ status for the column 0, puts it again in high impedance, increments the column number, set to 0 the variable coding the action to take. Of course, when the column number that has just been read is the highest for your keypad, the variable counting it (coltoread) must be set to 0. In a “pseudo” C++:
For more detail: Keypad 4×5 for microcontrollers v. 1.1 using PIC16F877