i2c总线程序(asm)
ORG 0000H
SCL EQU P1.0
SDA EQU P1.1
W85 EQU 0A0H;8583器件地址
W24 EQU 0A8H;24C04器件地址
SLA EQU 70H;器件地址寄存器
SUBA EQU 71H;片内地址寄存器
MTD EQU 1EH;传送数据寄存器
MRD EQU 4EH;读出数据寄存器
NUMBYTE EQU 72H;传送字节数寄存器
ACK BIT 73H;测试位
AJMP MAIN
;*********************************************************************
;开机显示1-8,当有按键(P0。4)按下,然后向24C02写数据然后再读入显示
;*********************************************************************
MAIN: MOV R0,#0F1H ;数码管显数
MOV R1,#07H ;数码管位选指针,从左到右点亮
MOV R2,#08H ;循环结束指针
LOOP: MOV P0,R0
MOV P2,R1 ;第八只数码管点亮,显0
LCALL DELAY ;
INC R0
DEC R1
DJNZ R2,LOOP
JNB P0.4,HH
AJMP MAIN
HH: LCALL DELAY3
JNB P0.4,SETI
AJMP MAIN
;**************************************************************
;向8583时钟芯片送入出始值2003/07/14/01/18:55:00
;出始值放入缓存器30H-3EH内
;对24C04写入的数据
;****************************************************************
SETI: MOV 30H,#00H ;秒的个位
MOV 31H,#00H ;秒的十位
MOV 32H,#05H ;分的个位
MOV 33H,#05H ;分的十位
MOV 34H,#08H
MOV 35H,#01H ;时的值
MOV 36H,#01H ; 周的值
MOV 37H,#04H
MOV 38H,#01H ;日期
MOV 39H,#07H
MOV 3AH,#00H ;月份
MOV 3BH,#03H
MOV 3CH,#00H
MOV 3DH,#00H
MOV 3EH,#02H ;年分
;********************************************************
;写入24C04
;********************************************************
WI2C: MOV P0,#06H
MOV P2,#07H
LCALL DELAY2 ;延时满足传输要求
LCALL ZZ ;字节转换,转换成8583字节格式
MOV SLA,#W24
MOV SUBA,#00H
MOV NUMBYTE,#07H ;送入口参数
LCALL IWRNBYTE ;写入24C04程序
LCALL DELAY
LCALL DELAY2 ;调用延时,满足要求
;**********************************************
;读24C04数据
;***************************************************
MOV SLA,#W24
MOV SUBA,#00H
MOV NUMBYTE,#07H ;送入口参数
LCALL IRDNBYTE ;读出24C04程序
LCALL DELAY2
K: LCALL DISPLAY1;显示数据
LCALL DELAY
MOV A,P0
ORL A,#0F0H
MOV P0,A
JNB P0.4,K2;是否有键按下
AJMP K
K2: LCALL DELAY3;去抖动
JNB P0.4,QQ
AJMP K
;**************************************************************
;对8583设置参数
;控制字为00H
;***********************************************************
QQ: MOV P0,#0F2H
MOV P2,#07H
LCALL DELAY2
LCALL DELAY
LCALL ZZ ;字节转换程序
MOV SLA,#W85
MOV SUBA,#00H
MOV 1EH,#00H
MOV 1FH,#00H ;初始化8583
MOV NUMBYTE,#07H ;送入口参数
LCALL IWRNBYTE ;送设置时间值给8583
LCALL DELAY2
LCALL DELAY
;************************************************************
;读出8583时钟和日历数据
;***************************************************************
K1: MOV SLA,#W85
MOV SUBA,#00H
MOV NUMBYTE,#07H ;送入口参数
LCALL IRDNBYTE ;读8583的数值
NOP
LCALL DISPLAY1;显示时钟和周次
LCALL DELAY
MOV A,P0
ORL A,#0F0H
MOV P0,A
JNB P0.7,K5;是否有翻页键按下,有则显示日历
AJMP K1
K5: LCALL DELAY3
JNB P0.7,K6
AJMP K1
K6: LCALL DISPLAY2;显示日历
MOV A,P0
ORL A,#0F0H
MOV P0,A
JNB P0.7,K7;是否又有翻页键按下,有则显示时钟
AJMP K6
K7: LCALL DELAY3
LCALL DELAY3
LCALL DELAY3;去抖动
JNB P0.7,K8
AJMP K6
K8: LJMP K1
;**************************************************************
;
; 以下程序为子程序
;
;**************************************************************
XX: MOV 50H,20H
MOV 51H,21H
MOV 52H,22H
MOV 53H,23H
MOV 54H,24H
MOV P0,#0F0H
RET
;********************************************************
;字节转换程序
;入口参数-把30H-3EH的数据转换成8583的格式,
;出口参数-存放在20H-24H中
;********************************************************
ZZ: MOV A,30H
ANL A,#0FH
MOV R0,A
MOV A,31H
ANL A,#0FH
SWAP A
ADD A,R0
MOV 20H,A ;将秒的字节给20H
MOV A,32H
ANL A,#0FH
MOV R0,A
MOV A,33H
ANL A,#0FH
SWAP A
ADD A,R0
MOV 21H,A ;将分的字节给21H
MOV A,34H
ANL A,#0FH
MOV R0,A
MOV A,35H
ANL A,#03H
SWAP A
ADD A,R0
MOV 22H,A ;将时的字节给22H
MOV A,37H
ANL A,#0FH
MOV R0,A
MOV A,38H
ANL A,#03H
SWAP A
ADD A,R0
MOV R1,A
MOV A,3BH
ANL A,#03H
SWAP A
MOV R0,#02H
LOOP6: RL A
DJNZ R0,LOOP6
ADD A,R1
MOV 23H,A ;将年,日的字节给23H
MOV A,39H
ANL A,#0FH
MOV R0,A
MOV A,3AH
ANL A,#01H
SWAP A
ADD A,R0
MOV R1,A
MOV A,36H
ANL A,#07H
SWAP A
RL A
ADD A,R1
MOV 24H,A ;将周,月的字节给24H
RET
;**********************************************
;无翻页键按下时的显示子程序-DISPLAY1
;显示数据在50H-54H中
;只显示周,时,分,秒
;50H-显示秒
;51H-显示分
;52H-显示时
;53H-显示年,日
;54H-显示周,月
;**********************************************
DISPLAY1: MOV P0, 50H
MOV P2,#00H ;显示秒个位
LCALL DELAY
MOV A,50H
SWAP A
MOV P0,A
MOV P2,#01H ;显示秒十位
LCALL DELAY
MOV P0,51H
MOV P2,#02H ;显示分个位
LCALL DELAY
MOV A,51H
SWAP A
MOV P0,A
MOV P2,#03H ;显示分十位
LCALL DELAY
MOV P0,52H
MOV P2,#04H ;显示小时个位
LCALL DELAY
MOV A,52H
SWAP A
MOV P0,A
MOV P2,#05H ;显示小时十位
LCALL DELAY
MOV A,54H
SWAP A
RR A
ANL A,#07H
MOV P0, A
MOV P2,#06H ;显示周个位
LCALL DELAY
MOV P0,#00H
MOV P2,#07H ;显示周十位
RET
;********************************************
;当有翻页键按下时的显示子程序-DISPLAY2
;用到了50H-54H
;只显示年,月,日
; 设置年时用到3CH,3DH,3EH
;********************************************
DISPLAY2: MOV P0,53H
MOV P2,#00H ;显示日的个位
LCALL DELAY
MOV A,53H
SWAP A
ANL A,#03H ;屏蔽无用位
MOV P0,A
MOV P2,#01H ;显示日的十位
LCALL DELAY
MOV P0,54H
MOV P2,#02H ;显示月的个位
LCALL DELAY
MOV A,54H
SWAP A
ANL A,#01H ;屏蔽无用位
MOV P0,A
MOV P2,#03H ;显示月的十位
LCALL DELAY
MOV A,53H
MOV R0,#02H
LOOP7: RL A
DJNZ R0,LOOP7 ;左移2位
ANL A,#03H ;屏蔽无用位
MOV P0,A
MOV P2,#04H ;显示年的第一位
LCALL DELAY
MOV P0,3CH
MOV P2,#05H ;显示年的第二位
LCALL DELAY
MOV P0,3DH
MOV P2,#06H ;显示年的第三位
LCALL DELAY
MOV P0,3EH
MOV P2,#07H ;显示年的第四位
LCALL DELAY
RET
;*************************************************************
;使用前要定义好SCL和SDA。在标准80C51模式
;(12 Clock)下,对主频要求是不高于12MHz(1个机器周期1us);若Fosc>12MHz
;则要增加相应的NOP指令数。
; 请注意
;占用内部资源: R0,R1,R2,R3,ACC,Cy。
; 程序里要做以下定义:
;使用前须定义变量: SLA 器件从地址 SUBA器件子地址
; NUMBYTE读/写的字节数 ,位变量ACK
;使用前须定义常量: SDA SCL 总线位 MTD 发送数据缓冲区首址
; MRD 接收数据缓冲区首址
;(ACK为调试/测试位,ACK为0时表示无器件应答)
;*************************************************************
;************************************************************
;启动I2C总线子程序
;***********************************************************
START: SETB SDA
NOP
SETB SCL ;起始条件建立时间大于4.7us
NOP
NOP
NOP
NOP
NOP
CLR SDA
NOP ;起始条件锁定时大于4us
NOP
NOP
NOP
NOP
CLR SCL ;钳住总线,准备发数据
NOP
RET
;****************************************************************
;结束总线子程序
;****************************************************************
STOP: CLR SDA
NOP
SETB SCL ;发送结束条件的时钟信号
NOP ;结束总线时间大于4us
NOP
NOP
NOP
NOP
SETB SDA ;结束总线
NOP ;保证一个终止信号和起始信号的空闲时间大于4.7us
NOP
NOP
NOP
RET
;***********************************************************************
;发送应答信号子程序
;***********************************************************************
MACK: CLR SDA ;将SDA置0
NOP
NOP
SETB SCL
NOP ;保持数据时间,即SCL为高时间大于4.7us
NOP
NOP
NOP
NOP
CLR SCL
NOP
NOP
RET
;**********************************************************************
;发送非应答信号
;************************************************************************
MNACK: SETB SDA ;将SDA置1
NOP
NOP
SETB SCL
NOP
NOP ;保持数据时间,即SCL为高时间大于4.7us
NOP
NOP
NOP
CLR SCL
NOP
NOP
RET
;*****************************************************************************
; 检查应答位子程序
; 返回值,ACK=1时表示有应答
;**************************************************************************8
CACK: SETB SDA
NOP
NOP
SETB SCL
CLR ACK
NOP
NOP
MOV C,SDA
JC CEND
SETB ACK ;判断应答位
CEND: NOP
CLR SCL
NOP
RET
;*************************************************************************
;发送字节子程序
;字节数据放入ACC
;每发送一字节要调用一次CACK子程序,取应答位
;**************************************************************************
WRBYTE: MOV R0,#08H
WLP: RLC A ;取数据位
JC WR1
SJMP WR0 ;判断数据位
WLP1: DJNZ R0,WLP
NOP
RET
WR1: SETB SDA ;发送1
NOP
SETB SCL
NOP
NOP
NOP
NOP
NOP
CLR SCL
SJMP WLP1
WR0: CLR SDA ;发送0
NOP
SETB SCL
NOP
NOP
NOP
NOP
NOP
CLR SCL
SJMP WLP1
;************************************************************************
;读取字节子程序
;读出的值在ACC
;每取一字节要发送一个应答/非应答信号
;************************************************************************
RDBYTE: MOV R0,#08H
RLP: SETB SDA
NOP
SETB SCL ;时钟线为高,接收数据位
NOP
NOP
MOV C,SDA ;读取数据位
MOV A,R2
CLR SCL ;将SCL拉低,时间大于4.7us
RLC A ;进行数据位的处理
MOV R2,A
NOP
NOP
NOP
DJNZ R0,RLP ;未够8位,再来一次
RET
;*********************************************************************
; 无子地址器件写字节数据
; 入口参数: 数据为ACC、器件从地址SLA
; 占用: A、R0、CY
;********************************************************************
IWRBYTE: PUSH ACC
IWBLOOP: LCALL START ;起动总线
MOV A,SLA
LCALL WRBYTE ;发送器件从地址
LCALL CACK
JNB ACK,RETWRB ;无应答则跳转
POP ACC ;写数据
LCALL WRBYTE
LCALL CACK
LCALL STOP
RET
RETWRB: POP ACC
LCALL STOP
RET
;****************************************************************
;无子地址器件读字节数据
;入口参数: 器件从地址SLA
;出口参数: 数据为ACC
;占用 A 、R0、R2 、CY
;*******************************************************************
IRDBYTE: LCALL START
MOV A,SLA ;发送器件从地址
INC A
LCALL WRBYTE
LCALL CACK
JNB ACK,RETRDB
LCALL RDBYTE ;进行读字节操作
LCALL MNACK ;发送非应信号
RETRDB: LCALL STOP ;结束总线
RET
;***************************************************************************
;向器件指定子地址写N个数据
;入口参数: 器件从地址SLA、器件子地址SUBA 、发送数据缓冲区MTD、发送字节数NUMBYTE
; 占用: A 、R0 、R1 、R3 、CY
;****************************************************************************
IWRNBYTE: MOV A,NUMBYTE
MOV R3,A
LCALL START ;起动总线
MOV A,SLA
LCALL WRBYTE ;发送器件从地址
LCALL CACK
JNB ACK,RETWRN ;无应答则退出
MOV A,SUBA ;指定子地址
LCALL WRBYTE
LCALL CACK
MOV R1,#MTD
WRDA: MOV A,@R1
LCALL WRBYTE ;开始写入数据
LCALL CACK
JNB ACK,IWRNBYTE
INC R1
DJNZ R3,WRDA ;判断写完没有
RETWRN: LCALL STOP
RET
;*********************************************************************
;向器件指定子地址读取N个数据
;入口参数: 器件从地址SLA、器件子地址SUBA、接收字节数NUMBYTE
;出口参数: 接收数MRD
;占用:A、 R0、 R1、 R2、 R3、 CY
;********************************************************************
IRDNBYTE: MOV R3,NUMBYTE
LCALL START
MOV A,SLA
LCALL WRBYTE ;发送器件从地址
LCALL CACK
JNB ACK,RETRDN
MOV A,SUBA ;指定子地址
LCALL WRBYTE
LCALL CACK
LCALL START ;重新起动总线
MOV A,SLA
INC A ;准备进行读操作
LCALL WRBYTE
LCALL CACK
JNB ACK,IRDNBYTE
MOV R1,#MRD
RDN1: LCALL RDBYTE ;读操作开始
MOV @R1,A
DJNZ R3,SACK
LCALL MNACK ;最后一字节发非应答位
RETRDN: LCALL STOP ;并结束总线
RET
SACK: LCALL MACK
INC R1
SJMP RDN1
;**********************************************************
;延时程序——DELAY:16MS 显示用
;DELAY2:约1S;
;DELAY3:130MS左右 去抖动
;
;**********************************************************
DELAY: MOV R7,#0FH
DL: MOV R6,#0FH
DL1: DJNZ R6,DL1
DJNZ R7,DL
RET
;*********************************************
DELAY2: MOV R7,#05H
DL6: MOV R5,#0FFH
DL4: MOV R6,#0FFH
DL5: DJNZ R6,DL5
DJNZ R5,DL4
DJNZ R7,DL6
RET
;*********************************************
DELAY3: MOV R7,#0FFH
DL2: MOV R6,#0FFH
DL3: DJNZ R6,DL3
DJNZ R7,DL2
RET
END
;***********************************************************
;************************************************************