实现矩阵式键盘输入、多LED动态输出显示
2.1 独立式和矩阵式键盘比较
独立式键盘虽然硬件、软件结构简单,但在按键数量较多的情况下,将占有较多的I/O端口,所以在按键数量较多的情况下,一般采用可以有效减少I/O端口数量的矩阵式键盘。矩阵式键盘又称为行列式键盘,采用行、列线结构,按键设置在行列线的交叉点上,如下图所示,H0~H3为四条行线,L0~L3为四条列线,在行列相交的每个交点上通过按键来连通,按键开关的一个触点连行线,一个触点连列线,从而组成4×4矩阵16键键盘
2.2 键编码及键值
(1) 用键盘连接的I/O线的二进制组合表示键码
例如用4行、4列线构成的16个键的键盘,可使用一个8位I/O口线的高、低4位口线的二进制数的组合表示16个键的编码。
如图所示,各键相应的键值为:
88H、84H、82H、81H
48H、44H、42H、41H
28H、24H、22H、21H
18H、14H、12H、11H
(2)顺序排列键码
如图所示,这种方法键值的形成要根据I/O线的状态作相应的程序处理。键码可按下式形成:
1111,1110,1101,1100
1011,1010,1001,1000
0111,0110,0101,0100
0011,0010,0001,0000
高四位对应的行首键码、行键值及低四位的列号、列键值如下表所示:
高四位
行首键码 行键值 D7 第3行 1100 1000_0000 D6 第2行 1000 0100_0000 D5 第1行 0100 0010_0000 D4 第0行 0000 0001_0000
低四位
列号 列键值 D3 第3列 0011 0000_1000 D2 第2列 0010 0000_0100 D1 第1列 0001 0000_0010 D0 第0列 0000 0000_0001
键值=行键值+列键值 例:42H(=D6+D1)=0100_0000H+0000_0010H=0100_0010H
键码=行首键值+列号 例:42H对应的键码为 1000+0001=1001
2.3 矩阵式键盘工作原理
①无按键按下时,行线输入高电平;
②有按键按下时,行线输入电平由与此行线相连的列线电平决定.
•如果列线输出低电平,则行线电平为低
•如果列线输出高电平,则行线电平为高
③为了确认按键位置,必须将行、列线配合使用。
对键盘的工作过程可分两步:
第一步是CPU首先检测键盘上是否有键按下;
第二步是再识别是哪一个键按下。
检测键盘上有无键按下可采用程序扫描方式和中断工作方式。
本文仅介绍程序扫描方式
一般情况下,在单片机应用系统中,键盘处理只是CPU工作的一部分。为了能及时发现有键按下CPU必须不算调用键盘处理程序,对键盘进行扫描,因此称为程序扫描方式。
①首先列线全输出0,判断是否有键按下。
•如果行线为全1,无按键按下
•如果行线非全1,有按键按下
②然后,让列线P1.4输出0,其它3条列线输出1,读行线状态。
•如果行线为全1,第一列无按键按下,继续扫描。
•如果行线非全1,可以判断按键在第0列,再根据为0的行线序号,可以确定按键具体的行号,停止扫描。
③如果第0列无按键按下,让列线P1.5口输出0,其它三条列线输出1,读行线状态,判断按键是否在第一列。
④为求取键码,在逐列扫描时,可用计数器记录下当前扫描列的列号,然后用行首键码加列号的办法计算。
下面以教材《单片机原理》P169举例
例: 12号键按下,对应第1行,第4列,则行首键码为01_000,列号为00_100
故键值为:01_000+00_100=01_100
R2(PA7~PA0) 列扫描字 |
R4 (列号) |
|
扫描第0列 |
FE(1111_1110) |
0 (000) |
扫描第1列 |
FD(1111_1101) |
1 (001) |
扫描第2列 |
FB(1111_1011) |
2 (010) |
扫描第3列 |
F7(1111_0111) |
3 (011) |
扫描第4列 |
EF(1110_1111) |
4 (100) |
扫描第5列 |
DF(1101_1111) |
5 (101) |
扫描第6列 |
BF(1011_1111) |
6 (110) |
扫描第7列 |
7F(0111_1111) |
7 (111) |
行首键码 |
|
第0行 | 00_000 (00H) |
第1列 | 01_000 (08H) |
第2列 | 10_000 (10H) |
第3列 | 11_000 (18H) |
键盘扫描子程序流程图:
编写键盘程序四步
(1)判断是否有键闭合
(2)去抖动
(3)求键值
(4)等待按键的释放
//本例代码
/******首先列线全输出0,判断是否有键按下。
如果行线为全1,无按键按下
如果行线非全1,有按键按下
******/
KS: MOV DPTR, #PA ;A口地址送入DPTR
MOV A, #00H ;全扫描字送A
MOVX @DPTR, A ;全扫描字送PA口,列全部输出0
INC DPTR ;指向C口
INC DPTR
MOVX A, @DPTR ;读入PC口(行)状态
CPL A ;变正逻辑:高电平表示有键按下
ANL A, #0FH ;屏蔽高4位
RET
KEY: LCALL KS ;判别有无键按下
JNZ K1 ;(A)不等于0,有键按下,转K1
LCALL DELAY ;无键按下调用延时子程序
AJMP KEY ;返回重新查询
K1: LCALL DELAY ;加长延时,消除键抖动
LCALL DELAY
LCALL KS ;再次查询有无键按下
JNZ K2 ; (A)不等于0,有键按下,转逐列扫描
AJMP KEY ;误读键,返回
/******然后,让列线PA0输出0,其它七条列线输出1,读行线状态。
如果行线为全1,第一列无按键按下,继续扫描。
如果行线非全1,可以判断按键在第一列,再根据为0的行线序号,可以确定按键具体的行号,停止扫描。
******/
K2: MOV R2, #0FEH ;首列扫描字送R2
MOV R4, #00H ;首列号送R4
K3: MOV DPTR, #PA ;A口地址送DPTR
MOV A, R2
MOVX @DPTR, A ;列扫描字送8155A口
INC DPTR
INC DPTR
MOVX A, @DPTR ;读取行扫描值
JB ACC.0, L1;(PC.0)=(ACC.0)=1,第0行无键按下,转查第一行
MOV A, #00H ;第0行有键按下,行首键号送A
AJMP LK ;转求键号
L1: JB ACC.1, L2 ;(PC.1)=(ACC.1)=1,第1行无键按下,转查第2行MOV A, #08H ;第1行有键按下,行首键号送A
AJMP LK ;转求键号
L2: JB ACC.2, L3 ;(PC.2)=(ACC.2)=1,第2行无键按下,转查第3行
MOV A, #10H ;第2行有键按下,行首键号送A
AJMP LK ;转求键号
L3: JB ACC.3, NEXT ;(PC.3)=(ACC.3)=1,第3行无键按下,转查下一列
MOV A, #18H ;第3行有键按下,行首键号送A
;开始求键码
LK: ADD A, R4 ;形成键码送A:键码=行首键码+列号
PUSH ACC ;键码入栈保存
K4: LCALL DELAY
LCALL KS ;等待键释放
JNZ K4 ;(A)不等于0,有键按下,按键未释放,等待
POP A ;键释放,弹出键码
RET
/******如果第一列无按键按下,让列线PA1口输出0,其它七条列线输出1,读行线状态,判断按键是否在第二列。
******/
;准备扫描下一列
NEXT: INC R4 ;修改列号,扫描下一列
MOV A, R2 ;PA口(列)数据送A
JNB ACC.7, KEY ;(PA.7)=(ACC.7)=0,8列扫描完,返回KEY RL A ;未扫描完,扫描字左移一位
MOV R2, A ;扫描字存R2
AJMP K3
;延时子程序
DELAY: MOV R7, #0FFH
LP0: MOV R6, #0FFH
LP: NOP
DJNZ R6, LP
DJNZ R7, LP0
RET
①实现效果一:按下键盘,键盘对应数字依次从左往右扫过数码管
如图,P0口为段选端,P3口为位选端
因为P0口内部没有上拉电阻,不能输出高电平,所以要接上拉电阻。其中用到的是RESPACK8排阻,有关数码管显示的问题,可参考其他大佬的文章。
ORG 0000H
LJMP START
ORG 0030H
START: MOV DPTR,#TABLE ;DPTR指向段码表首地址
MOV R7,#7FH ;设置动态显示扫描初值
S1: MOV A,#00H
MOVC A,@A+DPTR ;查表取得段码
CJNE A,#01H,S2 ;判断段码是否为结束符
SJMP START
S2: MOV B,A ;段码送B保存
MOV A,R7
RL A ;显示位扫描值左移1位
MOV P3,A ;显示位扫描值送P3口
MOV R7,A
MOV P0,B ;显示段码送P0显示
LCALL DELAY ;延时
INC DPTR
SJMP S1
DELAY: MOV R5,#20 ;延时子程序
D2: MOV R6,#20
D1: NOP
DJNZ R6,D1
DJNZ R5,D2
RET
TABLE: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H ;0-7共阴段码表
DB 01H ;结束符
END
说明:
数码管:
段选端连P0口,位选端连P1口
矩阵键盘:
行连P2口低三位,列连P3口低三位
LED:
用于观察数码管位选端
/*************矩阵式键盘输入**************/
KS: MOV A,#00H
MOV P3,A
CPL A ;变正逻辑:高电平表示有键按下
ANL A, #0FH ;屏蔽高4位
RET
;
KEY: LCALL KS ;判别有无键按下
JNZ K1 ;(A)不等于0,有键按下,转K1
LCALL DELAY1 ;无键按下调用延时子程序
AJMP KEY ;返回重新查询
K1: LCALL DELAY1 ;加长延时,消除键抖动
LCALL KS ;再次查询有无键按下
JNZ K2 ; (A)不等于0,有键按下,转逐列扫描
AJMP KEY ;误读键,返回
K2: MOV R2, #0FEH ;首列扫描字送R2
MOV R4, #00H ;首列号送R4
K3: MOV A, R2
MOV P3, A ;列扫描字送P3口
JB P2.0, L1 ;P2.0=1,第0行无键按下,转查第一行
MOV A, #00H ;第0行有键按下,行首键号送A
AJMP LK ;转求键号
;
L1: JB P2.1, L2 ;P2.1=1,第1行无键按下,转查第2行
MOV A, #03H ;第1行有键按下,行首键号送A
AJMP LK ;转求键号
L2:
JB P2.2, NEXT ;P2.2=1,第2行无键按下,转查第3行
MOV A, #06H ;第2行有键按下,行首键号送A
AJMP LK ;转求键号
LK: ADD A, R4 ;形成键码送A:键码=行首键码+列号
MOV R1,A ;键码A送R1保存
SJMP START
;准备扫描下一列
NEXT: INC R4 ;修改列号,扫描下一列
MOV A,R2 ;列数据送A
JNB P3.2, KEY ;P3.2=0,4列扫描完,返回KEY
RL A ;未扫描完,扫描字左移一位
MOV R2, A ;扫描字存R2
AJMP K3
DELAY1: MOV R7, #0FFH
LP0: MOV R6, #0FFH
LP: NOP
DJNZ R6, LP
DJNZ R7, LP0
RET
/*************多LED数码管动态输出显示**************/
START: MOV A,#00H
MOV DPTR,#TABLE ;DPTR指向段码表首地址
MOV R7,#7FH ;设置动态显示扫描初值
S1: ADD A,R1 ;获取键码
MOVC A,@A+DPTR ;显示键码值
MOV B,A ;段码送B保存
S2: MOV A,R7
RL A ;显示位扫描值左移1位
MOV P1,A ;显示位扫描值送P3口
MOV R7,A
MOV P0,B ;显示段码送P0显示
LCALL DELAY ;延时
JNB P1.7,K1
SJMP S2
DELAY: MOV R5,#200 ;延时子程序
D2: MOV R6,#248
D1: NOP
DJNZ R6,D1
DJNZ R5,D2
RET
TABLE: DB 06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH ;1-9共阴段码表
DB 01H ;结束符
END
②实现效果二:按下键盘,键盘对应数字显示在数码管对应位上
(ps:对应proteus电路图跟效果一演示电路图一致)
ORG 0000H
LJMP KEY
ORG 0030H
/*************矩阵式键盘输入**************/
KS: MOV A,#00H
MOV P3,A
CPL A ;变正逻辑:高电平表示有键按下
ANL A, #0FH ;屏蔽高4位
RET
;
KEY:
LCALL KS ;判别有无键按下
JNZ K1 ;(A)不等于0,有键按下,转K1
LCALL DELAY1 ;无键按下调用延时子程序
AJMP KEY ;返回重新查询
K1: LCALL DELAY1 ;加长延时,消除键抖动
LCALL KS ;再次查询有无键按下
JNZ K2 ; (A)不等于0,有键按下,转逐列扫描
AJMP KEY ;误读键,返回
K2: MOV R2, #0FEH ;首列扫描字送R2
MOV R4, #00H ;首列号送R4
K3: MOV A, R2
MOV P3, A ;列扫描字送P3口
JB P2.0, L1 ;P2.0=1,第0行无键按下,转查第一行
MOV A, #00H ;第0行有键按下,行首键号送A
AJMP LK ;转求键号
;
L1: JB P2.1, L2 ;P2.1=1,第1行无键按下,转查第2行
MOV A, #03H ;第1行有键按下,行首键号送A
AJMP LK ;转求键号
L2:
JB P2.2, NEXT ;P2.2=1,第2行无键按下,转查第3行
MOV A, #06H ;第2行有键按下,行首键号送A
AJMP LK ;转求键号
LK: ADD A, R4 ;形成键码送A:键码=行首键码+列号
MOV R1,A ;键码A送R1保存
SJMP START
;准备扫描下一列
NEXT: INC R4 ;修改列号,扫描下一列
MOV A,R2 ;列数据送A
JNB P3.2, KEY ;P3.2=0,4列扫描完,返回KEY
RL A ;未扫描完,扫描字左移一位
MOV R2, A ;扫描字存R2
AJMP K3 ;
DELAY1: MOV R7, #0FFH
LP0: MOV R6, #0FFH
LP: NOP
DJNZ R6, LP
DJNZ R7, LP0
RET
/*************多LED数码管动态输出显示**************/
START: MOV A,#00H
MOV DPTR,#TABLE ;DPTR指向段码表首地址
//MOV R7,#7FH ;设置动态显示扫描初值
S1: ADD A,R1 ;获取键码
MOVC A,@A+DPTR ;显示键码值
MOV B,A ;段码送B保存
MOV A,R1 ;获取位码
MOV DPTR,#TABLE1 ;DPTR指向位码表首地址
MOVC A,@A+DPTR ;显示位码值
MOV R7,A
S2: ;MOV A,R7
;RL A ;显示位扫描值左移1位
;MOV P1,A ;显示位扫描值送P3口
;MOV R7,A
MOV P1,R7 ;显示位码送P1显示
MOV P0,B ;显示段码送P0显示
LCALL DELAY ;延时
//JNB P1.7,K1
SJMP KEY
DELAY: MOV R5,#200 ;延时子程序
D2: MOV R6,#248
D1: NOP
DJNZ R6,D1
DJNZ R5,D2
RET
TABLE: DB 06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH ;1-9共阴段码表
DB 01H ;结束符
TABLE1: DB 0feH,0fdH,0fbH,0f7H,0efH,0dfH,0bfH,07fH ;8 位码表
DB 02H
END
最终演示效果在本人视频分区中,请移步观看