单片机干货,利用PCF8563设计多功能时钟
一、PCF8563基本介绍
PCF8563是Philips公司推出的一款工业级内含I2C总线接口功能的具有极低功耗的多功能时钟/日历芯片,具有定时器功能、多种报警功能、时钟输出功能及中断输出功能等。由于采用I2C总线接口方式,应用电路简洁,提高了电路的工作可靠性,目前已被广泛应用于电表、水表、煤气表、电话、传真等仪器仪表领域。
1. PCF8563特点与功能
芯片特点:
①宽电压范围:工作电压为1.0~5.5V;
②低休眠电流:典型值为0.25μA;
③可编程时钟输出:可输出时钟频率为32.768KHz、1024Hz、32Hz、1Hz;
④具有四种报警功能和定时器功能;
⑤内含复位电路、振荡器电路和掉电检测电路;
⑥开漏中断输出;
⑦400kHzI2C总线工作方式:其从地址读0A3H,写0A2H。
PCF8563典型电路为
二、任务描述
利用51单片机来虚拟出I2C总线接入带I2C总线的时钟接口器件PCF8563,再结合键盘矩阵和LED显示设计并制作出具有如下功能的实时时钟:
(1)自动计时,由6位LED显示器显示时、分、秒。
(2)具备校准功能,可以由按键设置当前时间。
(3)一年时差不超过5分钟。
(4)可以显示年、月、日。
(5)可以通过按键调整当前年、月、日。
1.计时方案
(1)方案一:软件控制
利用51单片机内部的定时/计数器进行中断定时,配合软件延时实现时、分、秒的计时。该方案节省硬件成本,且能够使读者在定时/计数器的使用、中断及程序设计方面得到锻炼与提高,但程序编写麻烦,并且由于技术的发展,以及设计思路的更新,这种方法在实际生产使用和现场开发中已不多采用。
(2)方案二:采用实时时钟芯片
针对计算机系统对实时时钟功能的普遍需求,各大芯片生产厂家陆续推出了一系列的实时时钟集成电路,如PCF8563、DS1287、DS1302等。这些芯片具备年、月、日、时、分、秒计时功能和多点定时功能,硬件接口方便,程序编写简单。此外,实时时钟芯片多数带有锂电池做后备电源,具备永不停止的计时功能。
本文将采用具有I2C总线接口的实时时钟器件PCF8563来实现时钟的计时。
2.键盘方案
键盘有两种设计方式:独立式按键和矩阵式键盘,本系统中用到了5个按键,一个功能键,一个设定键,一个上翻键,一个下翻键,一个确定键。
3.显示方案
对于实时时钟而言,显示显然是另一个重要的环节,本系统采用LED数码管动态显示。
三、硬件设计
1.电路原理图
实时时钟电路具体电路如图所示,基于实时时钟芯片采用PCF8563,配备6位LED显示和6个矩阵式接口键盘;报警电路由P1.3控制蜂鸣器来完成。
2.系统工作分析
本实时时钟具备以下功能:
(1)时钟显示
系统正常运行时,6位LED数码管从左到右依次显示时、分、秒,采用24小时计时。显示时间时时、分、秒之间用LED数码管的DP点隔开。
(2)按键控制
采用5个矩阵式键盘,一个功能键,一个设定键,一个上翻键,一个下翻键,一个确定键,用来设定时间。在整个程序设计中采用了一键多功能的作用,在软件中实现一个按键依据按下次数的不同实现不同的功能。各按键的功能如下:
①功能键
功能键用于进入几种功能模式,包含以下几种功能:①正常时间显示模式(包括时、分、秒);②年月日显示模式;③时间调整模式;④年月日调整模式。单片机主要工作在正常时间显示模式。
②设定键
设定键用于设定当前时间和日期,包括设定秒、分、时、日、月、年。当功能键进入调整时间模式时,用于设定当前时间。当功能键进行年月日调整模式时,用于设定当前日期。
③上翻和下翻键
用于调整时间时,增大和减小当前值,在本设计中不考虑连击效果,关于连击应用可自行思考。
④确定键
当设定完成后,采用确定键来使得当前输入时间有效(写入到PCF8563中)。
(3)时间显示
上电后,系统自动进入正常时间显示模式,从当前时间开始计时。当按下功能键时,进入到年月日显示模式,显示当前的年月日。进入调整时间模式,只显示当前要调整的秒、分或时,没有被调整的显示暗码。
(4)时间调整
按下功能键,进入时间调整模式,系统只显示秒的内容,其余4位LED均处于全暗状态,等待按键设置。此时按动上翻键后秒将会加一,此时按动下翻键后秒将会减一。按下确认键后,保存当前的设定值到PCF8563中,并处于当前模式。若再按动设定键则用来调整分钟,此时时钟和秒的4位LED指示均全暗,分钟显示当前的分钟,按上翻、下翻键后可以对分钟进行增一和减一调整,按下确认键后,保存当前的设定值到PCF8563中。再按下设定键则用来调整时,此时秒钟和分钟的4位LED指示均全暗,时显示当前的小时数,按上翻下翻键后可以对分钟进行增一或减一调整,按下确认键后,保存当前的设定值到PCF8563中,并处于当前模式。年月日的调整模式以及报警时间设定模式的操作同时间调整模式相似。
四、软件设计
1.软件设计思路
(1)定时动态扫描:
通常采用的循环动态扫描方法在使用时必须反复调用循环显示,甚至于在调整时间的按键程序中也应当常调显示子程序,否则显示在按键不松的情况下可能会出现短暂的"黑屏"现象,这种动态显示方式降低了CPU的工作效率。
系统中采用定时器来完成动态扫描显示。用T0定时器定2ms的时间间隔,每次定时时间到时就输出一位LED显示信号,定时器每中断6次后循环到第一位LED显示。在本项目采用定时器0用作显示定时,按方式1工作,每隔2ms溢出中断一次。
①定时器工作方式1
定时器的内部计数器为16位的计数器,当16位计数器计数满时,定时器溢出中断请求标志位TF置1。内部计数器的最大计数值为216=65536,定时时间如下:
T=(216-计数初值)Χ机器周期=(65536-计数初值)Χ12/fosc
本项目中单片机的晶振频率fosc=12MHZ,要求T0产生2ms的定时,用于6个数码管动态显示。使定时器产生2ms的定时,设定时器工作方式1,工作方式控制字为01H,定时器内部计数器为16位,则计数初值=65536-2000=63536=F830H;
②定时器初始值设置
程序采用中断方式,初始化程序如下:
;T0_INIT程序:初始化T0的数据寄存器和控制寄存器。
T0_INIT:MOV TMOD,#10H ;置方式控制字
MOV TL0,#30H
MOV TH0,#0F8H ;置定时器初值
SETB EA ;开CPU中断
SETB ET0 ;允许T0中断
SETB TR0 ;启动T0
RET
(2)按键设定程序
本任务中的按键只有5个,所以每个按键的功能就比较多,即按键都是多功能键,其按键设定子程序设计就比较复杂。下面以表格形式列出按键的各个功能,FUNCOU和SETCOU用来表示各个按键所处的不同状态。按键功能表
注:表格中"-"表示当前操作下此按键无效
(3)主程序
初始化与按键控制,读时间并对定时时间进行判断。主程序初始化后,就开始进行对PCF8563的读时间;读完后送显示缓冲区,然后开始依次扫描6个按键来判别是否进入相应的按键子程序中;再对PCF8563读时间完成主程序循环。
2.系统资源分配
为方便阅读程序,先对片内RAM及标志位的分配加以说明。
1、DISBUF:显示缓冲区:使用(30H~35H)6个RAM作LED的显示缓冲区,每一个缓冲区对一个LED,在定时时间到时就把6个缓冲区中的数送给相应的LED显示。
2、MRD:接收数据缓冲区首址:使用(60H~6AH)10个单元用来存放从PCF8563中读出来的时间信息。
3、MTD:发送数据缓冲区首址:使用(50H~5AH)10个单元用来存放要写入PCF8563中的时间信息。
4、SET_MRD:当前时间缓存区首址:使用(40H~4AH)10个RAM单元。在按键设定程序中用来保存当前从PCF8563中读出来的时间,利用上翻下翻按键来修改其中的值,当确认按键按下时,把SET_MRD中的信息备份到MTD并写入PCF8563。
5、FUNCOU,SETCOU(3AH~3BH):两个计数器:用于按键设定子程序中对当前功能和设定两个按键的按下次数进行计数,即表示当前处于何种设定状态。
6、KEY_MA(39H):用于保存当前按键的键码,以确定是哪个按键被按下。
7、KEY_F0(20H.0):用于表示当前是否进入按键设定,其值=1表示已进入按键设定。初值为0,并且在按键功能设定完后清零。
8、KEY_F1(20H.1):用于其值=1时表示功能键的第一次按下,其他情况下为0;初值为0,并且在按键功能设定完后清零。
3.软件流程图
根据上述工作流程,软件设计可分为以下几个功能模块:
1、主程序:主要包括:初始化程序、8563时钟写程序、8563读时钟程序、显示刷新子程序、按键测试子程序。流程图如图8-17所示。
2、初始化程序:包括定时器0、内部RAM、I/O、显示缓冲区、计数单元等的初始化。
3、定时器程序:为定时器0中断服务子程序,主要是完成循环显示。
4、按键扫描及设定:主程序中由按键扫描程序判定有无键按下,当有按键按下时调用按键设定子程序。按键设定子程序主要用来调整时间、调整年月日和调整报警时间。按键流程图如图8-18所示。
五、汇编源程序
SCL BIT P1.4 ;SCL:模拟I2C总线时钟线
SDA BIT P1.5 ;SDA:模拟I2C总线数据线
WAR BIT P1.7 ;PCF8563中断引脚
FMING BIT P1.3 ;蜂鸣器的控制引脚=0时鸣叫
KEY_F0 BIT 20H.0 ;用于表示当前是否进入按键设定,=1表示进入
KEY_F1 BIT 20H.1 ;=1时表示功能键的第一次按下
MTD EQU 50H ;MTD:发送数据缓冲区首址
MRD EQU 60H ;MRD:接收数据缓冲区首址
SLAW EQU 0A2H ;定义芯片写地址
SLAR EQU 0A3H ;定义芯片读地址
NUMBYT EQU 3EH ;定义传送数据字节数存储单元
DISBUF EQU 30H ;从30H到35H6个显示缓冲区
WEI_MA EQU 36H ;动态显示时位码的存放单元
DUAN_MA EQU 37H ;动态显示时段码的存放单元
DIS_COU EQU 38H ;6个数码管显示的计数指针
KEY_MA EQU 39H ;用于保存按键的键码值
FUN_COU EQU 3AH ;用于对功能按键的按下次数进行计数
SET_COU EQU 3BH ;用于对设定按键的按下次数进行计数
SLA EQU 3CH ;定义读/写地址存储单元
SUBA EQU 3DH ;器件子地址(直接给出为00H)
SET_MRD EQU 40H ;当前时间缓存区首址(用在按键设定子程序中)
ORG 0000H
LJMP MAIN
ORG 000BH
LJMP T0_TIMER ;定时T0显示中断入口
ORG 0050H
MAIN: mov sp,#6Fh
MOV R0,#7FH
CLR A
MAIN_1: MOV @R0,A
DJNZ R0,MAIN_1
lcall main_init ;调初始化程序
lcall WR_8563 ;调8563时钟写程序
main_2: LCALL RD_8563 ;调8563读时钟程序
JB KEY_F0,MAIN_3
;如果已进入按键设定,则不刷新显示缓冲区,由按键设定子程序来刷新
LCALL ShUAXIN ;调用刷新显示缓冲区子程序
main_3: LCALL KEY_TEST ;调用按键测试子程序
ljmp main_2
;主程序结束
;main_init初始化程序
main_init: MOV DIS_COU,#6 ;设定显示缓冲区和显示指针初值
MOV DUAN_MA,#DISBUF
MOV WEI_MA,#0CFH ;先显示最左边时的高位的数码管
CLR KEY_F0 ;首次程序进入时KEY_F0清零
CLR KEY_F1
MOV FUN_COU,#00H ;功能键计数器清零
MOV SET_COU,#00H ;设定键计数器清零
;定时器T0初始化
T0_INIT: MOV TMOD,#10H ;置方式控制字
;设定时钟初始值(设定为2008年10月20日星期1下午2点(14点)30分00秒,报警初始值为14点35分)。
LOAD_16: MOV MTD,#00H ;将子地址(SBUA)信息先发送过去
MOV MTD+1,#00H ;启动时钟
MOV MTD+2,#00H ;启动时钟
MOV MTD+3,#1FH ;设置报警及定时器中断,定时器中断为脉冲形式
MOV MTD+4,#30H ;置分初值
MOV MTD+5,#14H ;置时初值
MOV MTD+6,#20H ;日
MOV MTD+7,#01H ;
MOV MTD+8,#10H ;月
MOV MTD+9,#08H ;年
ret
;RD_8563读时钟程序:将秒、分、时、日、星期、月、年和报警信息等十个字节的时间信息读出并整理后放入MRD为首址的接收缓冲区中(其中关于星期的信息只读不要调整)。关于PCF8563的读写子程可参考任务三中的内容。
RD_8563: PUSH MTD ;把MTD中的内容保存
MOV MTD,#00H ;把子地址(SBUA)送到MTD缓冲区的首地址中
MOV SLA,#SLAW ;器件的写地址
MOV NUMBYT,#01H ;写一个字节
LCALL WRNBYT ;写入要读的内部单元地址
POP MTD ;恢复MTD中内容
MOV NUMBYT,#10H ;读出16个字节数
MOV SLA,#SLAR
LCALL RDNBYT
MOV A,MRD+2 ;取秒字节
ANL A,#7FH ;屏蔽无效位
MOV MRD+2,A
MOV A,MRD+3 ;取分钟字节
MOV MRD+3,A
MOV A,MRD+4 ;取小时字节
ANL A,#3FH ;屏蔽无效位
MOV MRD+4,A
MOV A,MRD+5 ;取日字节
MOV MRD+5,A
MOV A,MRD+6 ;取星期字节
ANL A,#07H ;屏蔽无效位
MOV MRD+6,A
MOV A,MRD+7 ;取月字节
ANL A,#1FH ;屏蔽无效位
MOV MRD+7,A
MOV A,MRD+8 ;取年字节
MOV MRD+8,A
;ShUAXIN刷新程序:将MRD缓冲区中秒、分、时三个字节由压缩BCD码转换成非压缩BCD码,同时存放在显示缓冲区DISBUF中。其中分和时的个位上小数点也要点亮,用于表示时间显示的区分点。
ShUAXIN: MOV R0,#DISBUF+5;
MOV R1,#MRD+2
MOV R2,#03H ;共有3个压缩BCD码
SHUAXIN_1: MOV A,@R1 ;秒信号BCD码送显示缓冲区,取秒信号
ANL A,#0FH ;取秒分时个位
MOV @R0,A ;秒个位送秒个位显示缓冲区
DEC R0
MOV A,@R1 ;取秒分时显示信号
ANL A,#0F0H ;取秒分时十位
SWAP A
MOV @R0,A
INC R1
DJNZ R2,SHUAXIN_1
mov a,31h
add a,#10
mov 31h,a
mov a,33h
mov 33h,a ;用于点亮分和时的个位上的小数点
;ShUAXINA刷新程序:将set_mrd缓冲区中秒、分、时三个字节由压缩BCD码转换成非压缩BCD码,同时存放在显示缓冲区DISBUF中。
ShUAXINA:MOV R0,#DISBUF+5;
MOV R1,#SET_MRD+2
SHUAXINA_1: MOV A,@R1 ;秒信号BCD码送显示缓冲区,取秒信号
DJNZ R2,SHUAXINA_1
;ShUAXINB刷新程序:将set_mrd缓冲区中日、月、年三个字节由压缩BCD码转换成非压缩BCD码,同时存放在显示缓冲区DISBUF中。
ShUAXINB:MOV R0,#DISBUF+5;
MOV R1,#SET_MRD+5
MOV A,@R1 ;日信号BCD码送显示缓冲区,取日信号
ANL A,#0FH ;取日分时个位
MOV @R0,A ;日个位送日个位显示缓冲区
MOV A,@R1 ;取日分时显示信号
ANL A,#0F0H
SWAP A ;取日分时十位
mov @r0,a
INC R1 ;让过星期寄存器
MOV A,@R1 ;月信号BCD码送显示缓冲区,取月信号
ANL A,#0FH ;取月分时个位
MOV @R0,A ;月个位送月个位显示缓冲区
MOV A,@R1 ;取月分时显示信号
SWAP A ;取月分时十位
MOV A,@R1 ;年信号BCD码送显示缓冲区,取年信号
ANL A,#0FH ;取年分时个位
MOV @R0,A ;年个位送年个位显示缓冲区
MOV A,@R1 ;取年分时显示信号
SWAP A ;取年分时十位
;WR_8563写入设定好的时钟初始值(每次时钟从2008年10月20日星期1下午2点(14点)30分00秒开始运行,报警时间为14点35分00秒)或由按键设定之后的时间信息。
WR_8563:LCALL LOAD_16 ;将10个寄存器内容装入发送缓冲区中
MOV SLA,#SLAW ;取器件地址
MOV NUMBYT,#10 ;写10个信息
LCALL WRNBYT ;写入
;RD_MRD把PCF8563中读出的16个信息放到SET_MRD中供按键设定子程序设定。
RD_MRD: MOV R0,#MRD
MOV R1,#SET_MRD
MOV R2,#10H
RD_MRD1:MOV A,@R0
MOV @R1,A
DJNZ R2,RD_MRD1
;WR_MRD把按键设定子程序中设定的10个值SET_MTD中的内容放到MTD发送缓冲区中以写PCF8563用。
WR_MRD: MOV R0,#MTD+1
MOV R2,#10
WR_MRD1:MOV A,@R1
DJNZ R2,WR_MRD1
;T0_TIMER:T0定时器中断服务程序:定时器定时时间为2ms,每次中断后改变一个数码管的显示。6个数码管从右向左的位码控制分别是P2.5~P2.0,所以从最左边向右边移位显示的位码初始值WEI_MA应为0CFH,中间位码的改变以RR来完成。数码管所对应的缓冲区分别是30H~35H,其中30H对应最左边的数码管,也就是30H对应的缓冲区中内容为小时高位的内容。
T0_TIMER:NOP ;T0的定时子程序
PUSH ACC
PUSH PSW
SETB RS0 ;使用第一组寄存器
MOV TH0,#0F8H
MOV TL1,#30H
MOV R0,DUAN_MA
MOV P2,#0FFH ;先熄灭所有的LED管
MOV A,@R0
MOV DPTR,#TAB
MOVC A,@A+DPTR
MOV P0,A ;送出段码
MOV P2,WEI_MA ;送位码
INC R0 ;改变显示缓冲区换成下个要显示的数
MOV DUAN_MA,R0 ;改变位码
MOV A,WEI_MA
RR A
MOV WEI_MA,A ;改变段码以便下次显示用
POP PSW
POP ACC
DJNZ DIS_COU,T0_TIMER1
MOV DIS_COU,#6
MOV WEI_MA,#0DFH ;先显示最左边时的高位的数码管
T0_TIMER1: RETI
;—————————按键测试程序开始
;KEY_TEST键盘测试子程序,利用线反转法测定键盘编码,有按键则调用KEY_SET按键设定子程序,无按键则返回。这个程序中所调用的子程序除KEY_SET外都请参考任务一中的程序。
KEY_TEST: LCALL KEY_ON
JZ KEY_OUT ;无按键返回
LCALL DL10ms
LCALL KEY_ON
JZ KEY_OUT
LCALL KEY_P ;用线反转法测出按键位置并计算按键编码
JZ KEY_OUT ;计算出错误码也返回
MOV KEY_MA,A
KEY_OFF: LCALL KEY_ON
JNZ KEY_OFF ;等待按键释放
LCALL KEY_SET ;若有按键按下并释放后调用按键设定子程序
KEY_OUT: RET
;—————————按键设定开始
;KEY_SET键盘设定子程序,给出每个按键在不同情况被按下时的不同功能
KEY_SET: NOP
JB KEY_F1,KEY_SET1
SETB KEY_F1 ;
LCALL RD_8563
LCALL RD_MRD ;把8563中读出的信息保存用于设定
KEY_SET1: MOV A,KEY_MA
MOV B,#03H
MUL AB
MOV DPTR,#KEY_S100
JMP @A+DPTR
;根据KEY_MA(键码)的不同值转到不同的按键处理程序中
KEY_S100: LJMP KEY_S10 ;转到功能键设定分支上
LJMP KEY_S20 ;转到设定键设定分支上
LJMP KEY_S30 ;转到上翻键设定分支上
LJMP KEY_S40 ;转到下翻键设定分支上
LJMP KEY_S50 ;转到确定键设定分支上
RET ;共6个按键,最后一个直接返回
;—————————功能按键的设定开始
;转到功能键设定分支上,把FUN_COU内容加1,如果FUN_COU=00H,则退出按键子程序
KEY_S10: INC FUN_COU
MOV A,FUN_COU
MOV B,#04H
DIV AB
MOV FUN_COU,B ;FUN_COU值加1并保存
MOV SET_COU,#00H ;SET_COU值清零
MOV A,B
JNZ KEY_F100
LCALL RD_8563 ;
LCALL SHUAXIN ;
MOV FUN_COU,#00H
MOV SET_COU,#00H
CLR KEY_F0
CLR KEY_F1 ;退出按键设定
RET ;重新显示正常时间
KEY_F100: LJMP KDISPLAY ;转到按键中的显示程序
;—————————设定按键的设定开始。
;转到设定键分支上,根据FUN_COU中的不同内容转到相应的分支上
KEY_S20: INC SET_COU
MOV A,SET_COU
MOV B,#03
MOV SET_COU,B ;SET_COU加1并保存的值
LJMP KDISPLAY ;跳转到按键后的显示功能
;—————————上翻按键的设定开始。
;转到上翻键分支上,根据FUN_COU中的不同内容转到相应的分支上
KEY_S30: MOV A,FUN_COU
MOV DPTR,#KEY_UP100
;根据FUN_COU(功能)的不同值转到不同的按键处理程序中
KEY_UP100: LJMP KDISPLAY ;上翻键中FUN_COU=00H直接转到按键后的显示功能
LJMP KDISPLAY ;上翻键中FUN_COU=01H直接转到按键后的显示功能
LJMP KEY_UP30 ;转到FUN_COU=02H调时间
LJMP KEY_UP40 ;转到FUN_COU=03H调日期
;---------------调时间
KEY_UP30: MOV A,SET_COU
MOV DPTR,#KEY_UP31
KEY_UP31: LJMP KEY_UP32 ;时间设定中SEC_COU=0的分支:秒加
LJMP KEY_UP33 ;时间设定中SEC_COU=1的分支:分加
LJMP KEY_UP34 ;时间设定中SEC_COU=2的分支:时加
KEY_UP32: MOV A,SET_MRD+2 ;秒调整
ADD A,#01H
DA A
MOV B,#60H
MOV SET_MRD+2,B ;调整为压缩BCD码,且以60为界
KEY_UP33: MOV A,SET_MRD+3 ;分调整
MOV SET_MRD+3,B ;调整为压缩BCD码,且以60为界
KEY_UP34: MOV A,SET_MRD+4 ;时钟调整
MOV B,#24H
MOV SET_MRD+4,B ;调整为压缩BCD码,且以24为界;
;---------------调日期
KEY_UP40: MOV A,SET_COU
MOV DPTR,#KEY_UP41
KEY_UP41: LJMP KEY_UP42 ;年月日调整中SET_COU=0的分支:年加
LJMP KEY_UP43 ;年月日调整中SET_COU=1的分支:月加
LJMP KEY_UP44 ;年月日调整中SET_COU=2的分支:日加
KEY_UP42: MOV A,SET_MRD+5 ;日调整(暂不考虑大小月,都按30天算)
MOV B,#31H
JNZ KEY_UP45
MOV A,#01H
KEY_UP45: MOV SET_MRD+5,A ;调整为压缩BCD码,且以30为界;
KEY_UP43: MOV A,SET_MRD+7 ;月调整
MOV B,#13H
JNZ KEY_UP46
KEY_UP46: MOV SET_MRD+7,A ;调整为压缩BCD码,且以12为界
KEY_UP44: MOV A,SET_MRD+8 ;年调整
CJNE A,#99H,KEY_UP47
MOV SET_MRD+8,#00H
LJMP KDISPLAY
KEY_UP47: ADD A,#01H
MOV SET_MRD+8,A ;调整为压缩BCD码,且以30为界;;调整
;—————————下翻按键的设定开始。
;转到下翻键分支上,根据FUN_COU中的不同内容转到相应的分支上
KEY_S40: MOV A,FUN_COU
MOV DPTR,#KEY_DO100
KEY_DO100: LJMP KDISPLAY ;下翻键中FUN_COU=00H直接转到按键后的显示功能
LJMP KDISPLAY ;下翻键中FUN_COU=01H直接转到按键后的显示功能
LJMP KEY_DO30 ;转到FUN_COU=02H时间减
LJMP KEY_DO40 ;转到FUN_COU=03H日期减;
KEY_DO30: MOV A,SET_COU
MOV DPTR,#KEY_DO31
KEY_DO31: LJMP KEY_DO32 ;时间设定中SEC_COU=0的分支:秒减
LJMP KEY_DO33 ;时间设定中SEC_COU=1的分支:分减
LJMP KEY_DO34 ;时间设定中SEC_COU=2的分支:时减
KEY_DO32: MOV A,SET_MRD+2 ;秒调整
CJNE A,#00H,KEY_DO35
MOV A,#60H
KEY_DO35: ADD A,#99H
MOV SET_MRD+2,A
DEC SET_MRD ;调整为压缩BCD码,且以60为界
KEY_DO33: MOV A,SET_MRD+3 ;分调整
CJNE A,#00H,KEY_DO36
KEY_DO36: ADD A,#99H
MOV SET_MRD+3,A
KEY_DO34: MOV A,SET_MRD+4 ;时钟调整
CJNE A,#00H,KEY_DO37
MOV A,#24H
KEY_DO37: ADD A,#99H
MOV SET_MRD+4,A
KEY_DO40: MOV A,SET_COU
MOV DPTR,#KEY_DO41
KEY_DO41: LJMP KEY_DO42 ;年月日调整中SET_COU=0的分支:年减
LJMP KEY_DO43 ;年月日调整中SET_COU=1的分支:月减
LJMP KEY_DO44 ;年月日调整中SET_COU=2的分支:日减
KEY_DO42: MOV A,SET_MRD+5 ;日调整(暂不考虑大小月,都按30天算)
CJNE A,#01H,KEY_DO45
MOV A,#31H
KEY_DO45: ADD A,#99H
MOV SET_MRD+5,A;调整为压缩BCD码,且以30为界;
KEY_DO43: MOV A,SET_MRD+7 ;月调整
CJNE A,#01H,KEY_DO46
MOV A,#13H
KEY_DO46: ADD A,#99H
MOV SET_MRD+7,A ;调整为压缩BCD码,且以12为界
KEY_DO44: MOV A,SET_MRD+8 ;年调整
ADD A,#99H
MOV SET_MRD+8,A
;—————————确定按键的设定开始。
;转到确定键分支上,根据FUN_COU中的不同内容转到相应的分支上
KEY_S50: MOV A,FUN_COU ;
MOV DPTR,#KEY_EN100
KEY_EN100: LJMP KDISPLAY ;确定键中FUN_COU=00H直接转到按键后的显示功能
LJMP KDISPLAY ;确定键中FUN_COU=01H直接转到按键后的显示功能
LJMP KEY_EN30 ;
LJMP KEY_EN30 ;保存设定数据
KEY_EN30: LCALL WR_MRD ;调用发送信息更新子程序(更新时分秒)
LCALL WR_8563 ;发送数据到PCF8563中
LJMP KDISPLAY ;SEC_COU=0、1时跳转到按键后的显示功能
;按键中根据FUN_COU和SET_COU的值转到不同的显示内容中,即在各种模式下时,要显示的内容也不相同,在按键调整时根据按键功能表可以看出,显示的变化是依据FUN_COU和SET_COU不同而变化的,在调秒、分、时、年、月、日时,要调整的数据也在不同的变化,但此时每一种状态下,整体显示的东西(整个界面)并没有发生变化,变化的只是某个数,所以在按键中采用了功能和显示分开的程序,这种方法程序相对长点,可使用起来非常方便。
KDISPLAY: MOV A,FUN_COU
;转到功能键设定分支上,根据FUN_COU中的不同内容转到相应的分支上
MOV DPTR,#KDIS_F100
;根据FUN_COU(功能)的不同值显示不同的内容
KDIS_F100: LJMP KDIS_F10 ;显示正常时间
LJMP KDIS_F20 ;显示日期
LJMP KDIS_F30 ;显示调整时间
LJMP KDIS_F40 ;显示调整日期
;正常显示程序———功能模式0
KDIS_F10: LCALL RD_8563
LCALL SHUAXIN ;刷新显示8563中保存的当前时分秒值
;年月日显示程序———功能模式1
KDIS_F20: LCALL SHUAXINB ;刷新显示SET_MRD中保存的当前年月日值
RET ;功能键中FUN_COU=1退出按键子程序
;时分秒的调整程序———功能模式2
KDIS_F30: MOV A,SET_COU
MOV DPTR,#KDIS_T100
;根据SET_COU中的值转到不同的调秒、分、时的分支中
KDIS_T100: LJMP KDIS_T10 ;转到显示调秒状态
LJMP KDIS_T20 ;转到显示调分状态
LJMP KDIS_T30 ;转到显示调时状态
KDIS_T10: LCALL SHUAXINA ;刷新当前时间
MOV 30H,#20 ;时钟和分钟的位置全暗用于调秒
MOV 31H,#20
MOV 32H,#20
MOV 33H,#20
KDIS_T20: LCALL SHUAXINA ;刷新当前时间
MOV 30H,#20 ;时钟和秒钟的位置全暗用于调分
MOV 34H,#20
MOV 35H,#20
KDIS_T30: LCALL SHUAXINA ;刷新当前时间
MOV 32H,#20 ;秒钟和分钟的位置全暗用于调时
;年月日的调整程序———功能模式3
KDIS_F40: MOV A,SET_COU
MOV DPTR,#KDIS_D100
;根据SET_COU中的值转到不同的调年、月、日的分支中
KDIS_D100: LJMP KDIS_D10 ;转到显示调日状态
LJMP KDIS_D20 ;转到显示调月状态
LJMP KDIS_D30 ;转到显示调年状态
KDIS_D10: LCALL SHUAXINB ;刷新当前年月日
MOV 30H,#20 ;年和月的位置全暗用于调日
KDIS_D20: LCALL SHUAXINB ;刷新当前年月日
MOV 30H,#20 ;年和日的位置全暗用于调月
KDIS_D30: LCALL SHUAXINB ;刷新当前年月日
MOV 32H,#20 ;日和月的位置全暗用于调年
;—————————按键程序结束
;延时10ms子程序:DL10ms(12MHz)
DL10ms: MOV R7,#10
DL1: MOV R6,#250
DL2: NOP
NOP
DJNZ R6,DL2
DJNZ R7,DL1
;TAB显示字形表
TAB:DB 0C0H,0F9H,0A4H,0B0H,99H,92H,82H,0F8H,80H,90H
;0 1 2 3 4 5 6 7 8 9
DB 40H,79H,24H,30H,19H,12H,02H,78H,00H,10H,0FFH
;0 1 2 3 4 5 6 7 8 9 灭
六、程序分析
1、该程序的执行结果是:上电系统显示"14.30.00",表示当前时间,并按每秒计时。当有按键时,根据按键的不同显示不同的内容。
2、主程序采用T0中断做显示程序,定时器定时2ms,定时时间常数是F830H,所以在主程序里或按键调整时可以不考虑按键所引起的"黑屏"问题。
3、程序中采用了三个显示刷新子程序,当正常显示时使用SHUAXIN子程序,用于显示正常时间;SHAUXINA主要用于按键调整时的时分秒的显示刷新;SHUAXINB主要用于按键调整时年月日的显示刷新。
查看评论 回复