Why Matrix Keypad?
Typically one port pin is required to read a digital input into the controller. When there are a lot of digital input that has to be read, it is not feasible to allocate one pin for each of them. This is when a matrix keypad arrangement is used to reduce the pin.
Therefore, the number of pins that are required to interface a given number of inputs decreases with increase in the order of the matrix.
Example: If the matrix is 2×2, you will need 2 pins for the rows and 2 pins for the columns in such a case there is no difference in the cost of reading that many inputs. But if you consider a 10×10 matrix you will just need 20 pins (10 for the rows and 10 for the columns) to read 100 digital inputs.
Matrix Keypad Interface Logic
Initially all switches are assumed to be released. So there is no connection between the rows and columns. When any one of the switches are pressed, the corresponding rows and columns are connected (short circuited). This will drive that column pin (initially high) low. Using this logic, the button press can be detected. The colors red and black is for logic high and low respectively. Here are the steps involved in determining the key that was pressed.
Step 1:
The first step involved in interfacing the matrix keypad is to write all logic 0′s to the rows and all logic 1′s to the columns. In the image, black line symbolizes logic 0 and red line symbolizes logic 1.
For now let us assume that, the circled key is pressed and see how the key press can be detected by a software routine.
Step 2:
Now the software has to scan the pins connected to columns of the keypad. If it detects a logic 0 in any one of the columns, then a key press was made in that column. This is because the event of the switch press shorts the C2 line with R2. Hence C2 is driven low.
Note: color of the lines indicate the logic values they return.
Step 3:
Once the column corresponding to the key pressed is located, the next thing that the software has to do is to start writing logic 1′s to the rows sequentially (one after the other) and check if C2 become high. The logic is that if a button in that row was pressed, then the value written to that row will be reflected in determined column (C2) as they are short circuited. Note: color of the lines indicate the logic values they return.
Step 4:
The procedure is followed till C2 goes high with logic high is written to a row. In this case, a logic high to the second row will be reflected in the second column.
Note: color of the lines indicate the logic values they return.
We already know the key press happened at column 2. Now we have detected that the key is in row 2. So, the position of the key in the matrix is (2,2)
Once this is detected, its up to us to name it or provide it with a task in the event of the key press.
Implementation with C
Now lets see how the above logic can be implemented in embedded C. Here is the program I wrote to test it. This code is for PIC microcontrollers with c18 lite version compiler. I as usual, used a lot of macros so if you are an Arduino user you could easily make some alterations to the code and use it. The basic concept for keypad scan is inside the while(1) loop.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
#include<p18f4520.h>
#include<delays.h>
#pragma config OSC=HS,WDT=OFF,FCMEN=ON,XINST=OFF,IESO=OFF,LVP=OFF
#define HIGH 1
#define LOW 0
#define R1 PORTBbits.RB0
#define R2 PORTBbits.RB1
#define R3 PORTBbits.RB2
#define R4 PORTBbits.RB3
#define C1 PORTBbits.RB4
#define C2 PORTBbits.RB5
#define C3 PORTBbits.RB6
#define C4 PORTBbits.RB7
#define SEG_EN1 LATCbits.LATC1
#define SEG_EN2 LATCbits.LATC0
/*-----Function Declaration--------*/
void seg_wrt(void);
void update(unsigned char);
unsigned char val = 0;
void main(void)
{
ADCON1 = 0x0F;
TRISD = 0x00;
TRISC = 0x00;
TRISB = 0xf0;
LATC = 0x00;
LATD = 0x00;
INTCON2bits.RBPU = LOW; // Enable Weak internal pull-ups in pin RB4 and RB5
while(1)
{
LATB = 0xf0;
if(C1 == LOW){
R1 = HIGH;
if(C1 == HIGH)
update(16);
else {
R2 = HIGH;
if(C1 == HIGH)
update(15);
else {
R3 = HIGH;
if(C1 == HIGH)
update(14);
else update(13);
}
}
}
if(C2 == LOW){
R1 = HIGH;
if(C2 == HIGH)
update(12);
else {
R2 = HIGH;
if(C2 == HIGH)
update(11);
else{
R3 = HIGH;
if(C2 == HIGH)
update(10);
else update(9);
}
}
}
if(C3 == LOW){
R1 = HIGH;
if(C3 == HIGH)
update(8);
else {
R2 = HIGH;
if(C3 == HIGH)
update(7);
else{
R3 = HIGH;
if(C3 == HIGH)
update(6);
else update(5);
}
}
}
if(C4 == LOW){
R1 = HIGH;
if(C4 == HIGH)
update(4);
else {
R2 = HIGH;
if(C4 == HIGH)
update(3);
else{
R3 = HIGH;
if(C4 == HIGH)
update(2);
else update(1);
}
}
}
seg_wrt();
} // end of while(1)
}
void update(unsigned char data)
{
val = data;
}
void seg_wrt() {
unsigned char lookup[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
long unit,ten;
long idx = 0;
unit = val/10;
ten = val%10;
for(idx=0;idx<100; idx++)
{
LATD = lookup[unit];
SEG_EN1 = HIGH;
Delay10TCYx(10);
SEG_EN1 = LOW;
LATD = lookup[ten];
SEG_EN2 = HIGH;
Delay10TCYx(10);
SEG_EN2 = LOW;
}
}
|
The above program is done with polling and utilizes the entire time of the controller to scan the keypad and display the data on the 7 segment displays. There is a cool feature on Microcontrollers called as the Interrupt on Change (IOC). As the name suggests, the controller will interrupt if it finds any change in a port. In PIC the whole of PORT B has this feature. By using the feature without any change in the hardware setup we can scan the keypad in the ISR and have more of the controllers time to do some thing useful.