⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcf8574key20.asm

📁 C8051F.rar
💻 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 + -