51单片机快速入门之矩阵按键
图片来源于网络:

原理其实很简单 坐标系理论
硬件链接
p10-p13都为高电平 1111
p14-p17都为低电平 0000
此时 p1=0x0f
当按钮按下之后 p1将不在=0x0f
检测程序开始执行,扫描是那个按钮被按下
根据上述图片假设按键 0 被按下
p1 != 0x0f 此时进入检测程序
p1=0x7f 0111 1111 此时 p1.7 为 低电平
由图可以看出当c d e f 任意一个键位按下,电路导通,
电路导通低电平之后,低电平的优先级通常是最高的
说以这里会导致p10-p14 io口接地
我们只需要检测此时 p1是否还=0x7f 即可知道该行按钮是否按下
这里我们按下的是按键 0 所以 继续往下检测
p1=0x bf 1011 1111 p1.6为低
p1=0x df 1101 1111 p1.5为低
p1=0x ef 1110 1111 p1.4为低
因按键0被按下 所以此时 p1.0 被拉低 p1此时=0xee 1110 1110
可以看出实际上我们是在检测p10-p13是否被拉低
因此我们可以用到
按位与 &
对于两个二进制数的每一位,按位与运算遵循以下规则:
1 & 1
结果为1
1 & 0
结果为0
0 & 1
结果为0
0 & 0
结果为0
由上述我们可以知道
当p1=0x7f p1=0x bf p1=0x df p1=0x ef
所以我们用按位与运算
tmp=0x7f;//设定一个中间变量
检测P1==tmp;
如果不等于执行下方取低四位操作
JC=P1 & 0xf //按位 与 运算 这个运算只取低四位,其他不关心
十六进制数
0x7F
。在二进制中,0x7F
表示为0111 1111
。
JC = tmp & 0xF;
对tmp
和十六进制数0xF
(在二进制中表示为0000 1111
)执行按位与运算。按位与运算的规则是,只有当两个对应的二进制位都为1时,结果的对应位才为1,否则为0。因此,tmp & 0xF
的计算过程如下:0111 1111 (tmp)
& 0000 1111 (0xF)0000 1111
所以,
tmp & 0xF
的结果是0000 1111
,在十六进制中表示为0x0F
,在十进制中表示为15
。
因按键0被按下 所以此时 p1.0 被拉低 p1此时=0xee 1110 1110
所以p1此时等于0xee
JC=P1 & 0xf
1110 1110 (tmp)
& 0000 1111 (0xF)
----------
0000 1110
0000 1110
,在十六进制中表示为 0x0E
参考十六进制映射表
每个十六进制数字对应四位二进制数字的映射如下:
0 -> 0000
1 -> 0001
2 -> 0010
3 -> 0011
4 -> 0100
5 -> 0101
6 -> 0110
7 -> 0111
8 -> 1000
9 -> 1001
A -> 1010
B -> 1011
C -> 1100
D -> 1101
E -> 1110
F -> 1111
结合上述我们可以写出如下完整程序
#include <STC89C5xRC.H>
unsigned char i,j,tmp;//设定变量
unsigned char BUTTON[]={0xef,0xdf,0xbf,0x7f} ;//p1.4 p1.5 p1.6 p1.7
unsigned char LOWJC[]={0xe,0xd,0xb,0x7}; //p1.0 p1.1 p1.2 p1.3 低四位
unsigned char btnum[4][3] = {
{'0', '1', '2', '3'}, //第0行 p1.4
{'4', '5', '6', '7'}, //第1行 p1.5
{'8', '9', 'A', 'B'}, //第2行 p1.6
{'C', 'D', 'E', 'F'} //第3行 p1.7
}; 第0列,第1列,第2列,第3列
p1.0 p1.1 p1.2 p1.3
void main(){
while(P1 !=0x0f ) //当按钮被按下,P1不等于初始状态 满足条件,执行检测程序
{
for(i=0;i<4;i++)//从p1.7开始往下查
{
if(P1 !=BUTTON[i]) //当检测到P1状态与表格中不相等时执行
{ tmp=P1 & 0xf; //取低四位,并获取 i 行
for (j=0;j<4;j++)//通过后四位查表
{ if(tmp==LOWJC[j])
{ //满足条件获取列 j
输出 btnum[i][j];
//假设按键0被按下 i=0
//j=0
btnum[0][0] 就会得到 字符 0
}
}
}
}
}
}
第二种办法,直接建立字符段码

我们知道1110 1110 时为按钮 0
1110 1101时为按钮1
建立表格:
unsigned char buttonCodes[] = {
0xEE, // 按钮 0 (1110 1110)
0xED, // 按钮 1 (1110 1101)
0xEB, // 按钮 2 (1110 1011)
0xE7, // 按钮 3 (1110 0111)
0xDE, // 按钮 4 (1101 1110)
0xDD, // 按钮 5 (1101 1101)
0xDB, // 按钮 6 (1101 1011)
0xD7, // 按钮 7 (1101 0111)
0xBE, // 按钮 8 (1011 1110)
0xBD, // 按钮 9 (1011 1101)
0xBB, // 按钮 A (1011 1011)
0xB7, // 按钮 B (1011 0111)
0x7E, // 按钮 C (0111 1110)
0x7D, // 按钮 D (0111 1101)
0x7B, // 按钮 E (0111 1011)
0x77 // 按钮 F (0111 0111)
};
剩下的就是进入检测 判断
for(i=0;i<16;i++)
{
if(P1== buttonCodes[I])
{输出 i 数字 }
}