📄 pcf8574key20.asm
字号:
;PCF8574KEY20.ASM 20键扫描加6位数码显示的实验程序B:
;R1,存放扫描行的键值:各行首键的值分别为 #0,#4,#8,#12 (共有四行)
;R2,存放PB口送扫描的数。
;R5,存放扫描列的键值: #0,#1,#2,#3 (共有四列)
$include(c8051f020.inc)
SLA1W EQU 70H ;PCF8574 A片写的节点地址(可选地址开关:000H)
SLA1R EQU 71H ;PCF8574 A片读的节点地址
SLA2W EQU 72H ;PCF8574 B片写的节点地址 (可选地址开关:001H )
SLA2R EQU 73H ;PCF8574 B片读的节点地址
SLA3W EQU 76H ;PCF8574 C片写的节点地址 (可选地址开关:011H)
SLA3R EQU 77H ;PCF8574 C片读的节点地址
SLA EQU 60H
SLAW EQU 62H
SLAR EQU 63H
NUMBYT EQU 61H ;被传送的字节数存放单元
MTD EQU 20H ;发送数据缓冲区首地址
MRD EQU 30H ;接收字节缓冲区首地址
VSCL EQU P0.1 ;模拟I2C总线时钟线
VSDA EQU P0.0 ;模拟I2C总线数据线
ORG 0
AJMP BEGIN
ORG 30H
BEGIN: ACALL INIT ;初始化,将显示缓冲区(71H~76H)全置#0FFH(非数据)
ACALL INITA ;作为输入用的芯片PCF8574A(U1)应先将口锁存器置为高电平
MAIN: MOV WDTCN,#0DEH ;禁止看门狗定时器
MOV WDTCN,#0ADH;
MOV XBR2,#40H ;C8051F交叉开关使能
ACALL INITA ;作为输入用的芯片应先初始化
LCALL KEY0 ;查询功能键F1(PA4),F2(PA5),F3(PA6),F4(PA7)是否按下?
ACALL KEY20 ;调用判键子程序(针对小键盘上的20个键),查询是否有键按下?
ACALL DISP ;调用显示子程序,动态显示71H~76H中的值
MOV A,6CH ;处理四个功能键(F1,F2,F3,F4)的子程序。功能键值在6CH中
CJNE A,#10H,PP1
ACALL BELL1 ;按下F0键,蜂鸣器响一声(模拟F1功能)
PP1: CJNE A,#20H,PP2
ACALL BELL2 ;按下F1键,蜂鸣器响二声(模拟F2功能)
PP2: CJNE A,#40H,PP3 ;
ACALL BELL3 ;按下F2键,蜂鸣器响三声(模拟F3功能)
PP3: CJNE A,#80H,MAIN
ACALL BELL4 ;按下F3键,蜂鸣器响四声(模拟F4功能)
AJMP MAIN
;查询键按的子程序( 针对小键盘上的16个数字键,共有四行、四列)
KEY20: MOV R5,#0 ;(R5) = 列号,其值为#0,#1,#2,#3
MOV R2,#0FEH
KEY20S: MOV A,R2 ;往PB口送扫描数据:#0FEH
ACALL WRITEB
ACALL READA ;读A口
MOV A,MRD ;读PA口,判断按键在那一行?
MOV R1,#0 ;(R1)=行值:#0,#4,#8,#12
JNB ACC.0,REL0 ;第0行有键按下,行首键值R1为#0。转REL0处理
MOV R1 ,#04H
JNB ACC.1,REL0 ; 第1行有键按下,行首键值R1为#4。转REL0处理
MOV R1,#08H
JNB ACC.2, REL0 ; 第2行有键按下,行首键值R1为#8。转REL0处理
MOV R1, #0CH
JNB ACC.3 , REL0 ; 第3行有键按下,行首键值R1为#12。转REL0处理
MOV R1,#0
MOV A, R2
RL A
MOV R2,A ;准备扫描下一列(PB口),共有四列:#0,#1,#2,#3
INC R5 ;列的键值加1
CJNE R2, #0EFH, KEY20S ;第三列未扫描完,返回继续检测有无按键
RET ;第三列已扫描完,返回主程序
;有键按下,重设显示区
REL0: LCALL BELL ;有键按下,鸣笛
ACALL KEY20A ;消除按键抖动
MOV A, R5 ;取按键数值:行首值(#0,#4,#8,#12)加列值(#0,#1,#2,#3)
ADD A, R1 ;(R5)+(R1)=按键位置对应的数值(#0,#1,….#E,#F)
PUSH ACC ;保存键值(行值加列值)
MOV A , 71H ;将按键数值存入71H~76H中
CJNE A , #0FFH, RE1 ;71H中有数(非#0FFH),说明四位地址有效,转RE1,
;应将新数存入75H,76H中去
MOV 71H, 72H ;71H中无数((71H)=# 0FFH),数据从后位移向前位
MOV A, 71H ;键入的是四位地地址,应存入71H~74H中
CJNE A , #0FFH , KY4
SJMP KY5
KY4: CJNE A, #8, KY2 ;在71H中的数据应小于8。因为四位地址不能大于7FFFH
SJMP KY3
KY2: JNC KY3 ;即是说71H中的数不能出现#8,#9,#0A,#0B,#0C,#0D,#0E,#0F
KY5: MOV 72H,73H ;按一键,地址往前移动一位
MOV 73H,74H
POP ACC
MOV 74H,A ;将最新键入的数存入74H中,看作地址用
RET
KY3: MOV 71H,#0 ; 若四位地址值大于7FFFH,地址非法,应将四位地址改写为0000H
MOV 72H,#0
MOV 73H,#0
MOV 74H,#0
POP ACC
RET
RE1: POP ACC
MOV 75H,76H ;75H,76H中存放两位数值。76H向75H移动
MOV 76H , A ;将最新键入的数存入76H
RET
;消除抖动子程序
KEY20A: ACALL READA ;读A口
MOV A,MRD
CJNE A, #0FFH, KEY20A ;KEY20是否有键按下,有返回KEY20A
ACALL DEL0 ;延时消除误判
MOVX A, @DPTR
CJNE A,#0FFH,KEY20A ;再判是否有键按下,有则返回KEY20A等待
RET
INIT: MOV 71H,#6 ;在RAM 71H~76H 中充入#0FFH
MOV A,#0FFH
INDP: MOV @R0,A
INC R0
DJNZ R4,INDP
RET
READA: MOV SLAW,#SLA1W ;从A口输入的数据
MOV SLAR,#SLA1R
WRA: MOV SLA,SLAR
MOV NUMBYT,#1
LCALL RDNBYT
RET
INITA: MOV SLAW,#SLA1W ;初始化:置输入口锁存器为高电平
MOV SLAR,#SLA1R
MOV MTD,#0FFH
MOV SLA,SLAW
MOV NUMBYT,#1
LCALL WRNBYT
RET
WRITEB: MOV SLAW,#SLA2W ;将数据送器件B
MOV SLAR,#SLA2R
ACALL WRBC
RET
WRITEC: MOV SLAW,#SLA3W ;将数据送器件C
MOV SLAR,#SLA3R
ACALL WRBC
RET
WRBC: MOV MTD,A
MOV SLA,SLAW
MOV NUMBYT,#1
LCALL WRNBYT
RET
;查看F1,F2,F3,F4(PA4~PA7)是否有键按下?如有,则存入6CH中
KEY0: ACALL READA ;读A口
MOV A,MRD
ANL A,#0F0H ;只取高四位(F1,F2,F3,F4对应的位在高四位中)
CJNE A,#0F0H,K1
RET
K1: CPL A
MOV 6CH,A ;将功能键值存入6CH中
RET
BELL: CLR P3.4
ACALL DELY
SETB P3.4
RET
DELY: MOV R6,#20H ;延时子程序
TM: MOV R5,#0
DJNZ R5,$
DJNZ R6,TM
RET
DELYL: MOV R4,#4 ;长延时子程序
TT1: MOV R5,#0H
TT2: MOV R6,#0
DJNZ R6,$
DJNZ R5,TT2
DJNZ R4,TT1
RET
BELL1: ACALL BELL ;鸣笛一声,模拟功能键F1的功能
RET
BELL2: MOV R7,#2 ; 鸣笛二声,模拟功能键F2的功能
DF2: ACALL BELL
ACALL DELYL
DJNZ R7,DF2
RET
BELL3: MOV R7,#3 ; 鸣笛三声,模拟功能键F3的功能
DF3: ACALL BELL
ACALL DELYL
DJNZ R7,DF3
RET
BELL4: MOV R7,#4 ; 鸣笛四声,模拟功能键F4的功能
DF4: ACALL BELL
ACALL DELYL
DJNZ R7,DF4
RET
;显示六位数码管的子程序
DISP: MOV R2,#01H ; 字位码在R2中,(R2)= #1,#2,#4,#8
MOV R5,#6 ;显示6个数
MOV R0,#71H ;71H为要显示的数据区的首地址
DSP1: MOV A,R2
ACALL WRITEB ;B口输出作为六位数码管的位选
MOV A,@R0 ;将要显示的数值送入A中
DSP2: MOV DPTR,#BCD ;查表
MOVC A,@A+DPTR ;查表,查出要显示的数值的字形码
ACALL WRITEC ;C口输出字形码
ACALL DEL0 ;延时
MOV A,R2
RL A
MOV R2,A ; 字位码指向下一位
INC R0 ;准备显示下一位
DJNZ R5, DSP1
RET
BCD: DB 3FH,06H,5BH,4FH ;显示数值0,1,2,3
DB 66H,6DH,7DH,07H ;4,5,6,7
DB 7FH,6FH,77H,7CH ;8,9,A,B
DB 39H,5EH,79H,71H ;C,D,E,F
DB 73H,3EH,31H,6EH ;P,U,Z,Y
DB 40H,00H ;- ,灭
DEL0: MOV R6,#00H ;延时子程序
TMA: MOV R7,#01
DJNZ R7,$
DJNZ R6,TMA
RET
WRNBYT: MOV R3,NUMBYT
LCALL ST ;启动I2C总线
MOV A,SLA ;发送SLAW字节
LCALL WRBYT
LCALL CACK ;检查应答位
JB F0,WRNBYT ;非应答位则重发
MOV R1,#MTD
WRDA: MOV A,@R1
LCALL WRBYT
LCALL CACK
JB F0,WRNBYT
INC R1
DJNZ R3,WRDA
LCALL STOP
RET
WRBYT: MOV R0,#08H ;8为数据长度送R0中
WLP: RLC A ;发送数据左移,使发送位入C
JC WR1 ;判断发送"1"还是"0",发送"1"转WR1
AJMP WR0 ;发送"0"转WR0
WLP1: DJNZ R0,WLP ;8位是否发送完,未完转WLP
RET ;8位发送完结束
WR1: SETB VSDA ;发送"1"程序段
SETB VSCL
NOP
NOP
CLR VSCL
CLR VSDA
AJMP WLP1
WR0: CLR VSDA ;发送"0"程序段
SETB VSCL
NOP
NOP
CLR VSCL
AJMP WLP1
RDNBYT: MOV R3,NUMBYT
LCALL ST ;发送启动位
MOV A,SLA ;发送寻址字节(读)
LCALL WRBYT
LCALL CACK ;检查应答位
JB F0,RDNBYT ;非正常应答时重新开始
RDN: MOV R1,#MRD ;接收数据缓冲区首址MDR入R1
RDN1: LCALL RDBYT ;读入一个字节到接收数据缓冲中
MOV @R1,A
DJNZ R3,ACK ;N节读完否?未完转ACK
LCALL MNACK ;N个字节读完发送非应答位A
LCALL STOP ;发送停止信号
RET ;子程序结束
ACK: LCALL MACK ;发送应答位
INC R1 ;指向下一个接收数据缓冲单元
SJMP RDN1 ;转读入下一个字节数据
RDBYT: MOV R0,#08H ;8位数据长度如R0
RLP: SETB VSDA ;置VSDA为输入方式
SETB VSCL ;使VSDA上数据有效
MOV C,VSDA ;读入VSDA引脚状态
MOV A,R2 ;读入"0"程序段,由C拼装入R2中
RLC A
MOV R2,A
CLR VSCL ;使VSCL=0可继续接收数据位
DJNZ R0,RLP ;8位读完否?未读完转RLP
RET
STOP: CLR VSDA ;停止I2C总线数据传送
SETB VSCL
NOP
NOP
SETB VSDA
NOP
NOP
CLR VSDA
CLR VSCL
RET
MACK: CLR VSDA ;发送应答位
SETB VSCL
NOP
NOP
CLR VSCL
SETB VSDA
RET
MNACK: SETB VSDA ;发送非应答位
SETB VSCL
NOP
NOP
CLR VSCL
CLR VSDA
RET
CACK: SETB VSDA ;应答位检查
SETB VSCL
CLR F0
MOV C,VSDA
JNC CEND
SETB F0
CEND: CLR VSCL
RET
ST: SETB VSDA ;启动I2C总线
SETB VSCL
NOP
NOP
CLR VSDA
NOP
NOP
CLR VSCL
RET
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -