51单片机 4×4键盘矩阵控制条形LED显示+ Proteus仿真

51单片机 4×4键盘矩阵控制条形LED显示+ Proteus仿真


  • Proteus仿真
    在这里插入图片描述
  • Keil工程导入的是汇编文件
    在这里插入图片描述

实例代码

注意这是通过汇编代码写的程序,文件后缀格式是.asm格式

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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// 矩阵键盘控制LED灯
// 4X4矩阵键盘连到P1口上
// 16个LED灯依次连到P2和P3口上
// IO口作为输入口时,要先给他置1才能接受外接的信号,遇1则1,遇0则0
ORG 00H
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
// 初始化工作
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
START:
// 使LED全灭状态
MOV R1,#0FFH
MOV R2,#0FFH
MOV P2,R1
MOV P3,R2
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
// 主循环
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
MAIN_LOOP:
MOV A,#0F0H //使低四位(行)为0,高四位(列)为1,同时让列脚处于输入状态
MOV P1,A
CALL SCAN_KEYS
JMP MAIN_LOOP

// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
// 检测键盘
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
SCAN_KEYS:
MOV A,P1
CJNE A,#0F0H,CHECK_KEY //无键被按下时返回,结束此次检测
RET
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
// 按键处理过程
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
CHECK_KEY:
CALL LOCATE_COL
CALL DELAY
FRESH_STATE:
MOV A,P1
// 由于在检测行时,更换了P1的初始值为0FH,所以这里与0FH比较,而不是#0F0H
CJNE A,#0FH,FRESH_STATE //去抖,直到键被松开时(相等)执行下面的语句
// 列序号存在R1中,行序号在R2中
CALL SHOW_LED
RET
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
// 定位按钮所在列
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
LOCATE_COL:
// 先定位列,根据低四位为零时,找哪一列对应的脚本是零即可
CJNE A,#0E0H,COL_2 //检测是否为第一列
MOV R1,#1
JMP LOCATE_ROW
COL_2:
CJNE A,#0D0H,COL_3 //检测是否为第二列,若不是跳至检测下一列
MOV R1,#2
JMP LOCATE_ROW
COL_3:
CJNE A,#0B0H,COL_4 //检测是否为第三列
MOV R1,#3
JMP LOCATE_ROW
COL_4: //检测是否为第四列
CJNE A,#70H,RETURN
MOV R1,#4
JMP LOCATE_ROW

// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
// 定位按钮所在行
// 使高四位(列)为零,低四位(行)为1,检测低四位中哪个变为了零,即可定位行数
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
LOCATE_ROW:
MOV A,#0FH
MOV P1,A
MOV A,P1
CJNE A,#0EH,ROW_2
MOV R2,#1
RET
ROW_2:
CJNE A,#0DH,ROW_3
MOV R2,#2
RET
ROW_3:
CJNE A,#0BH,ROW_4
MOV R2,#3
RET
ROW_4:
CJNE A,#07H,RETURN
MOV R2,#4
RET
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
//显示相应的LED状态
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
SHOW_LED:
// 列序号减1然后乘以4加行序号等于键的序号

MOV A,R1 //刷新灯的状态
SUBB A,#1
MOV B,#4
MUL AB //结果低8位在A里
ADD A,R2 //得到键序号
MOV R3,A
// 当键序号为8时,则直接让上面(P2口)8个全亮, 下面8个(P3口)全灭
CJNE A,#8,CAL_LED
MOV R1,#00H
MOV R2,#0FFH
MOV P2,R1
MOV P3,R2
RET
CAL_LED:
SUBB A,#8 //带借位减法,若有借位则进(借)位标识Cy为1
JNC SET_LED
// 分支1:A小于8,减的结果小于0时,则只让P2口亮相应数目的灯,下面P3口全灭
MOV A,R3
MOV R1,A
MOV R2,#0FFH
MOV P3,R2
MOV A,#0FFH
P2_WEI_LED:
CLR C
RLC A
DJNZ R1,P2_WEI_LED
MOV P2,A
RET

SET_LED:
// 分支2:键序号大于8时,让P2口的灯全亮,下面P3口亮相应数目(减的结果)的灯

MOV R1,#00H
MOV R2,A
MOV P2,R1
MOV A,#0FFH
P3_WEI_LED:
CLR C
RLC A
DJNZ R2,P3_WEI_LED
MOV P3,A
RET

// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
RETURN:
RET
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
// 延时30ms
// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
DELAY:
MOV R5,#30
D1: MOV R6,#20
D2: MOV R7,#248
DJNZ R7,$
DJNZ R6,D2
DJNZ R5,D1
RET

END

//注:1)模块化编程,Call和RET的配对使用,完成一个子过程,并相互嵌套,完成复杂的任务过程,编写过程也是一步步实现,每实现一步通过仿真测试成功,再做下一步,确保了程序质量。2)深入理解单片机I/O口的输入输出,先置高电位,使该引脚处理接受输入状态,(原理根据期电路结构, 为0时并不能接收到外接电平的)3)代码段SCAN\_KEYS:理解矩形键盘的扫描过程,先给P1口赋值为# 0F0H,即低四位(行)为0,高四位为1,然后不断地读取P1口的值,若值仍保持原值#0F0H,则说明没有键被按下,若值发生变化,则进一步判断高四位中哪个变为低电平,则对应的列中的键被按下,即找到列序号,依据同样的道理,此时让低四位为1,高四位为0,然后看低四位中哪一个变为0,即确定行数,有了行序号和列序号即可确定键的序号,进而通过LED灯的状态来点亮相应数目的灯(此过程中又涉及减法及移位实现对应数目的灯变亮)4)代码段SHOW\_LED:实现了键序号到灯个数的转换,涉及了SUBB,RLC等操作。5)代码段CHECK\_KEY:检测键和去抖过程的结合,先执行定位键,然后再延时,直到键被松开。

看不懂汇编代码,我来上C代码

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
#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int

uchar code DSY_CODE[]=
{
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0x00
};
uchar code KeyCodeTable[]=
{
0x11,0x12,0x14,0x18,0x21,0x22,0x24,0x28,0x41,0x42,0x44,0x48,0x81,0x82,0x84,0x88
};

void Delay()
{
uchar i;
for(i=0;i<200;i++);
}

uchar Keys\_Scan()
{
uchar sCode,kCode,i,k;
P1 = 0xf0;
if((P1&0xf0)!=0xf0)
{
Delay();
if((P1&0xf0)!=0xf0)
{
sCode = 0xfe;
for(k=0;k<4;k++)
{
P1 = sCode;
if((P1&0xf0)!=0xf0)
{
kCode = ~P1;
for(i=0;i<16;i++)
{
if(kCode == KeyCodeTable[i])
return i;
}
}
else
sCode = \_crol\_(sCode,1);
}
}
}
return 255;
}

void main()
{
uchar KeyNO = 255;
uchar i,P2_LED,P3_LED;
while(1)
{
KeyNO = Keys\_Scan();
if(KeyNO != 255)
{
P2_LED = 0xff;
P3_LED = 0xff;
for(i=0;i<=KeyNO;i++)
{
if(i<8)
P3_LED>>=1;
else
P2_LED>>=1;
}
P3 = P3_LED;
P2 = P2_LED;
}
}
}


程序源码和仿真资源

1
2
3
链接:https://pan.baidu.com/s/142p0mqDFjiIqHmOv37WMaA 
提取码:tx8m