Many embedded applications require a large number of keys connected to a computing system. For example – a PC keyboard, Cell Phone keypad and Calculators.
If we connect a single key, we just connect it directly to a GPIO. But what when want to connect, say 10 or 100 keys directly MCUs. Most MCUs won’t have so many IO pins. The ATmega8 23 GPIOs. Moreover, connecting so many keys on a one-one basis will have the following problems:
- It will eat up precious i/o line.
- MCU to Keypad interface will contain lots of wires.
The 4×4 Matrix Keypad
To save us from all these troubles, some genius people came up with a clever technique – called the Multiplexed Matrix Keypad. In this arrangement of keypad, the keys are connected in the matrix style, i.e., rows and columns like shown below:
The algorithm is actually very simple. The first thing we need to do is
- Connect all the Row pins (R1-R4) to the MCU as input with internal pull-ups enabled
- Connect all the Column pins (C1-C4) to the MCU as output
Now we put the following logic in a loop:
- Set C1 to LOW and all other column lines to HIGH Z state
- Read all the Row pins one by one
- Move to the next column
As you can see in the image above C1 is made LOW while all other Columns are in HIGH Z State. We can read the Value of R1 to R4 to get their pressed status. If they are high the button is NOT pressed. As we have enabled internal pullups on the Row pins, these pullups keep their value high when they are floating (that means NOT connected to anything). But when a key is pressed it is connected to LOW line from the column thus making it LOW.
All we need to do now is monitor which of the four Row pins is low. Since C1 is low, if we get R1 as low, it means button 1 is pressed. Similarly R2 will give us button 5, R3 -> 9 and R4 -> 13
After that we make the C1 High Z again and make C2 LOW. And read R1 to R4 again. This gives us status of the second column of keys. Similarly we scan all columns.
Doing it all in AVR
Each i/o port in AVR has three related registers PORTx, DDRx and PINx. For example port A has
- PORTA Port Driver – when any bit is set to 1 it appears as HIGH i.e. 5v . But this is the case only if that bit is OUTPUT. If it is input, setting any bit to 1 enables the internal pullup on that bit.
- DDRA DATA DIRECTION REGISTER – Make any pin on than port as IN or OUT. When bit is 1 it represents Output. When bit is 0 it represents Input. Input state is also called tristate or high Z state.
- PINA – Read it to get the level (HIGH or LOW) at the actual i/o pin. It is read when the pin is made input.
So now you know
- How to make any i/o line Input(high Z) or Output.
- How to enable internal pullup register on input lines.
- How to read value that is present on input lines.
In the follow up post, we will see why it is important to make the columns LOW and in High Z state or Tristate. I will also share the code with you !!
Vivek is a Senior Embedded Engineer at Robert Bosch. He has been working on Embedded Systems for the past 10 years. He loves to share his knowledge and train those who are interested. Nerdyelectronics.com was started out of this interest.