📄 pcf8563.asm
字号:
; ======PCF8563初始化设置=====================================================
RTCINIT:ACALL I2C_BG ;启始位
MOV A, #10100010B ;发送目的器件地址,PCF8563
ACALL I2C_O
MOV A, #00H ;准备写入控制寄存器1、2
ACALL I2C_O
MOV A, #00H ;REGISTER 1 = 00
ACALL I2C_O
MOV A, #00H ;REGISTER 2 = 00
ACALL I2C_O
ACALL I2C_ED
CLR C
RET
; ======USE R0, R2, A, B=====================================================
TIME_G: ACALL I2C_BG
MOV A, #10100010B ;先发送[写]命令
ACALL I2C_O
MOV A, #02 ;发送秒寄存器编号
ACALL I2C_O
SETB SCL ;I2C_O返回时SCL=0,所以需要置 1
NOP
NOP
ACALL I2C_BG ;这样I2C_BG才能正常执行
MOV A, #10100011B ;发送读命令
ACALL I2C_O
MOV B, #07H
MOV R0, #RTCBUF ;取得时间,从PCF8563取得时间到缓冲区
TIME_G1:MOV DPL, #08H ;取得一个字节
TIME_G2:SETB SDA ;准备用 SDA 输入,所以先将其置 1。
SETB SCL ;SCL=1
NOP
NOP
MOV C, SDA ;SDA 已经有效,取到 C 中
RLC A ;C->ACC.0
CLR SCL ;SCL=0
DJNZ DPL, TIME_G2 ;得到的一个字节在 ACC 中
MOV @R0, A
INC R0
MOV A, B
XRL A, #01H
JZ TIME_G3 ;是最后一个字节,就不发确认位
CLR SDA
TIME_G3:SETB SCL ;不向 I2C 发送 ACK 位,而是一个空的时钟
NOP
NOP
NOP
CLR SCL
SETB SDA
DJNZ B, TIME_G1 ;取得不止一个字节
ACALL I2C_ED
MOV RTCBUF+4, RTCBUF+5 ;将月临时保存到星期的位置
MOV RTCBUF+5, RTCBUF+6 ;将年临时保存到月的位置
ANL RTCBUF+0, #7FH ;避免出现不合理数值
ANL RTCBUF+1, #7FH
ANL RTCBUF+2, #3FH
ANL RTCBUF+3, #3FH
ANL RTCBUF+4, #1FH
RET
; ===================================================================
; 时间设定,年、月、日、时、分、秒
TIME_A: ACALL KEYSCN
MOV A, R7
XRL A, #01H
MOV R6, #00H
JZ TIME_A2 ;长时间按键(>1秒)
MOV A, R7
ADD A, #-23 ;小于0.2秒
JC TIME_AA ;时间太短,返回
MOV R6, #01H ;自动循环显示一遍时间,年月日时分秒
TIME_A2:ACALL TIME_G ;从DS1302取得当前时间,放到缓冲区
MOV LCDBUF, #0BH ;'t'
MOV R0, #RTCBUF+5 ;设定缓冲区时间, 从年开始
MOV R2, #1AH ;符号'n'-'S'
MOV R3, #00H ;保存各个上下限的表格的指针
TIME_AL:MOV A, R3 ;根据R3计算当前值的上下限
MOV DPTR, #TM_LH
MOVC A, @A+DPTR
MOV R4, A ;R4放下限
MOV A, R3
INC A
MOVC A, @A+DPTR
MOV R5, A ;R5放上限
MOV LCDBUF+1, R2
TIME_AM:MOV A, @R0
ACALL BYTE_D
ACALL DISP
CJNE R6, #01H, TIME_A3
MOV B, #04
TIME_AN:ACALL DELAY
DJNZ B, TIME_AN
AJMP TIME_AS
TIME_A3:JB SETKEY, $
ACALL KEYSCN
JB F0, TIME_A3 ;按键无效
CJNE R7,#01H,TIME_A4 ;按键时间短,调整当前值
TIME_AS:INC R2
DEC R0
INC R3
INC R3
CJNE R3, #12, TIME_AL
CJNE R6, #01H, TIME_A6
AJMP TIME_A9
TIME_A4:MOV A, @R0
ADD A, #01H
DA A
CJNE A, 05H, TIME_A5
MOV A, R4
TIME_A5:MOV @R0, A
AJMP TIME_AM
TIME_A6:MOV RTCBUF+6, RTCBUF+5 ;将年送回到正确的位置
MOV RTCBUF+5, RTCBUF+4 ;将年送回到正确的位置
ACALL TIME_S ;将缓冲区时间保存到DS1302
TIME_A9:ACALL DISP_0
MOV LCDBUF+3, #00H
TIME_AA:RET
; 0BH-->'t',
TM_LH: DB 0,50H, 1,13H, 1,32H, 0,24H, 0,60H, 0,60H
; USE R7, A
KEYSCN: ACALL TM0_ST ;用T0计时
MOV R7, #30
KEYSCN0:JNB TF0, KEYSCN1
CLR TF0
DJNZ R7, KEYSCN1
MOV R7, #1
KEYSCN1:JNB SETKEY, KEYSCN0
MOV A, #20
ACALL DELAY
JNB SETKEY, KEYSCN0
CLR F0
CJNE R7, #30, KEYSCNE
SETB F0
KEYSCNE:RET
; ======USE R0, R2, R3, A, B===============================================
TIME_S: ACALL I2C_BG ;启始位
MOV A, #10100010B ;发送目的器件地址,PCF8563
ACALL I2C_O
MOV A, #02H ;准备写入寄存器2-8
ACALL I2C_O
MOV R0, #RTCBUF
MOV R2, #07H
TIME_S1:MOV A, @R0
ACALL I2C_O
INC R0
DJNZ R2, TIME_S1
ACALL I2C_ED
RET
;======= I2C SUBROUTINE =================================================================
; I2CD_W, I2CD_R :LAYER 1
; I2C_O, I2C_I ;LAYER 2
; I2C_BG, I2C_ED ;LAYER 3
;========================================================================================
; 向存储器写入几个字节,最多8个字节。
; 入口:DPTR 要写的EEPROM存储单元地址。
; R0 指向要写入字节在RAM的首地址。
; B 写入字节个数。
; 出口:如果 C=1,说明写出错。
I2CD_W: ACALL I2C_BG
MOV A, #10100000B ;写命令
ACALL I2C_O
JC I2CD_WE ;C=1, 未收到 ACK 位,出错,不再继续处理,直接返回
MOV A, DPH
ACALL I2C_O
JC I2CD_WE
MOV A, DPL
ACALL I2C_O
JC I2CD_WE
I2CD_WL:MOV A, @R0
ACALL I2C_O
JC I2CD_WE
INC R0
DJNZ B, I2CD_WL
ACALL I2C_ED
CLR C
RET
I2CD_WE:ACALL I2C_ED
SETB C
RET
;===============================================================================
; 从 EEPROM 读入几个字节
; 入口:DPTR 要读的存储单元地址。
; R0 指向要写入字节在RAM的首地址。
; B 写入字节个数。
; 出口:读到的内容在缓冲区中,如果 C=1,说明读出错。
I2CD_R: ACALL I2C_BG
MOV A, #10100000B ;先发送[写]命令
ACALL I2C_O
JC I2CD_RE ;C=1, 未收到 ACK 位,出错,不再继续处理,直接返回
MOV A, DPH ;发送地址低位
ACALL I2C_O
JC I2CD_RE
MOV A, DPL ;发送地址低位
ACALL I2C_O
JC I2CD_RE
ACALL I2C_BG
MOV A, #10100001B ;发送读命令
ACALL I2C_O
JC I2CD_RE
I2CD_IB:MOV DPL, #08H ;取得一个字节
I2CD_IL:SETB SDA ;准备用 SDA 输入,所以先将其置 1。
SETB SCL ;SCL=1
NOP
NOP
MOV C, SDA ;SDA 已经有效,取到 C 中
RLC A ;C->ACC.0
CLR SCL ;SCL=0
DJNZ DPL, I2CD_IL ;得到的一个字节在 ACC 中
MOV @R0, A
INC R0
MOV A, B
XRL A, #01H
JZ I2CD_GO ;是最后一个字节,就不发确认位
CLR SDA
NOP
I2CD_GO:SETB SCL ;不向 EEPROM 发送 ACK 位,而是一个空的时钟
NOP
NOP
NOP
CLR SCL
SETB SDA
DJNZ B, I2CD_IB ;取得不止一个字节
ACALL I2C_ED
CLR C
RET
I2CD_RE:ACALL I2C_ED
SETB C
RET
;向 IIC 总线发送一个字节
I2C_O: PUSH B
MOV B, #08H
I2C_OLP:RLC A ;ACC.7 -> C
MOV SDA, C
SETB SCL ;SCL=1
NOP
NOP
NOP
CLR SCL ;SCL=0
DJNZ B, I2C_OLP
SETB SDA ;准备接收 ACK 位
SETB SCL ;SCL=1, [SDA=1]
NOP
NOP
MOV C, SDA
CLR SCL
POP B
RET ;C=1, 未收到 ACK 位,出错, C=0, 收到 ACK 位,正常
;向 IIC 总线发送开始位
I2C_BG: SETB SCL
NOP
NOP
CLR SDA
NOP
CLR SCL
SETB SDA
RET
;向 IIC 总线发送停止位
I2C_ED: CLR SDA
SETB SCL
ACALL I2C_ED9 ;5
ACALL I2C_ED9 ;5
SETB SDA
NOP
CLR SCL
I2C_ED9:RET
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -