📄 new_clock_gai.asm
字号:
;************PH-I型51MCU学习系统实现的时钟与日历***************
;********单片机型号:AT89E564RD 晶振频率:11.0592MHz***********
;*****Copyright@1988-2009 Andery, All Rights Reserved**********
ORG 0000H
;********定义变量******************
weixuan EQU 8000H ;显示用位选地址
duanxuan EQU 9000H ;显示用段选地址
LEDBUF EQU 30H ;30H到37H存储区用来存放要显示时刻的段码
hour1 EQU 38H ;存放时钟的小时
minute1 EQU 39H ;存放时钟的分钟
second1 EQU 3AH ;存放时钟的秒
C100US EQU 3BH ;存放10000的低字节,用于控制秒增加的速度
hour2 EQU 3DH ;存放闹钟的小时
minute2 EQU 3EH ;存放闹钟的分钟
second2 EQU 3FH ;存放闹钟的秒
BUFCAL EQU 40H ;40H到4CH存储2009_01_01__共12位
SECTMP EQU 4EH ;控制日期循环显示次数的秒变量,和当前秒比较以判断三次是否到了
MINTMP EQU 4FH ;控制日期循环显示次数的分钟变量,和当前秒比较以判断三次是否到了
year1 EQU 50H ;存放年数的千、百前两位
year2 EQU 51H ;存放年数的十、个后两位
month EQU 52H ;存放月份
date EQU 53H ;日期
hourall EQU 54H ;小时容器,作为小时显示缓冲
minuteall EQU 55H ;分钟容器,作为分钟显示缓冲
secondall EQU 56H ;秒容器,作为秒显示缓冲
keys EQU 57H ;键值寄存器
keytime bit 58H ;闹铃进入和退出状态标志位
alarm bit 59H ;闹铃设置有效标志位 alarm为1则闹钟有效,为0则无效
keystate bit 5AH ;按键状态寄存位,有键按下时为1,无键按下时为0
mark1 bit 5BH ;闹铃状态标志位
TIME_CAL bit 5CH ;日历/时钟标志位
killmark bit 5DH ;消除闹钟标志位
MARK BIT 5EH ;用来闹铃响应时间是否达到了规定的12s
COUNT EQU 5FH ;用来帮助闹铃响应的计时
TICK EQU 10000 ;溢出10000次
T100US EQU 156 ;100微秒的初值
;*****************************************************************************
LJMP MAIN
ORG 000BH ;定时器0的中断向量
LJMP T0INT
ORG 0060H
;*************主程序**********************
MAIN:
CLR keystate
MOV sp,#70h ;栈指针初始化
MOV TMOD,#22H ;定时器0工作于方式2
MOV TH0,#T100US
MOV TL0,#T100US ;Timer0计数器载入初值
MOV TH1,#T100US
MOV TL1,#T100US ;Timer1计数器载入初值
MOV IE,#10001010B ;EA=ET1=ET0=1,ET2=ES=EX1=EX0=0,中断优先级为TF0>TF1>INT0>INT1>TF2
MOV hour1,#00H
MOV minute1,#00H
MOV second1,#00H ;时钟时、分、秒初始化
MOV hour2,#00h
MOV minute2,#00h
MOV second2,#00h ;闹钟时、分、秒初始化
MOV year1,#20
mov year2,#9
mov month,#1
mov date,#1 ;日历初始化为2009.01.01
MOV C100US+1,#HIGH(TICK)
MOV C100US,#LOW(TICK) ;溢出位初始化
MOV SECTMP,#0
MOV COUNT,#0
SETB TR0 ;启动timer0计数器
clr alarm
clr keytime
clr TIME_CAL
clr mark1
CLR MARK
clr killmark
BEGIN:
MOV hourall,hour1
MOV minuteall,minute1
MOV secondall,second1
jb mark1,next0 ;mark1为1则处于闹铃状态,为0则不在
jmp next
next0:
MOV hourall,hour2
MOV minuteall,minute2
MOV secondall,second2
jmp next
next:
LCALL key ;先检测一次键盘
LCALL dokey ;进行键盘处理
LCALL JUDGE ;将要显示的数转化成相应的段码
LCALL DIS_JUDGE ;存完后显示一次
JNB alarm,beginend ;若闹铃标志位为0,则重复循环
LCALL voice ;为1则闹铃
beginend:
LJMP BEGIN ;不断重复主程序
;*******判断显示日期还是时间***************************************
JUDGE:
JB TIME_CAL,DATE_trans ;取时间/日期切换标志位(0表示时间显示,1为日期显示)
;*******将当前的小时、分、秒转换成对应的段码***********************
TIME_trans:
MOV A,hourall
MOV B,#10
DIV AB
CALL TIME_LOCATE
MOV LEDBUF,A ;存小时的十位
MOV A,B
CALL TIME_LOCATE
MOV LEDBUF+1,A ;存小时的个位
MOV LEDBUF+2,#40H ;显示--
MOV A,minuteall
MOV B,#10
DIV AB
CALL TIME_LOCATE
MOV LEDBUF+3,A ;存分钟的十位
MOV A,B
CALL TIME_LOCATE
MOV LEDBUF+4,A ;存分钟的个位
MOV LEDBUF+5,#40H ;显示--
MOV A,secondall
MOV B,#10
DIV AB
CALL TIME_LOCATE
MOV LEDBUF+6,A ;存秒的十位
jnb alarm,trans1
mov a,LEDBUF+6
MOV LEDBUF+6,A
trans1:
MOV A,B
CALL TIME_LOCATE
MOV LEDBUF+7,A ;存秒的个位
jnb alarm,trans2
mov a,LEDBUF+7
MOV LEDBUF+7,A
trans2:
RET
;*************将当前的日期转换成对应的段码*******************
DATE_trans:
MOV 44H,#0F7H
MOV 47H,#0F7H ;年月日间隔处显示_
MOV 4AH,#0FFH
MOV 4BH,#0FFH ;末两位显示空格
MOV A,YEAR1
MOV B,#10
DIV AB
LCALL GET_DISP_SEG
MOV BUFCAL,A ;取年份的千位移入显示缓存第一位
MOV A,B
MOVC A,@A+DPTR
MOV BUFCAL+1,A ;取年份的百位移入显示缓存第二位
MOV A,YEAR2
MOV B,#10
DIV AB
LCALL GET_DISP_SEG
MOV BUFCAL+2,A ;取年份的十位移入显示缓存第三位
MOV A,B
MOVC A,@A+DPTR
MOV BUFCAL+3,A ;取年份的个位移入显示缓存第四位
MOV A,MONTH
MOV B,#10
DIV AB
LCALL GET_DISP_SEG
MOV BUFCAL+5,A ;取月份的十位移入显示缓存第六位
MOV A,B
MOVC A,@A+DPTR
MOV BUFCAL+6,A ;取月份的个位移入显示缓存第七位
MOV A,DATE
MOV B,#10
DIV AB
LCALL GET_DISP_SEG
MOV BUFCAL+8,A ;取日期的十位移入显示缓存第9位
MOV A,B
MOVC A,@A+DPTR
MOV BUFCAL+9,A ;取日期的个位移入显示缓存第10位
RET
;************时钟用查表子程序***************************
TIME_LOCATE:
MOV DPTR,#TABLE
MOVC A,@A+DPTR
RET
;************定义时钟用查找表***************************
TABLE:
DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH
;------- 0---1---2---3---4---5---6---7---8---9
;************日历用查找子程序***************************
GET_DISP_SEG:
MOV DPTR,#TABLE2
MOVC A,@A+DPTR
RET
;************定义日历用查找表***************************
TABLE2:
DB 0C0H,0F9H,0A4H,0B0H,99H,92H,82H,0F8H,80H,90H
;---------0----1----2----3---4---5---6---7----8---9
;************显示子程序*********************************
DIS_JUDGE:
JB TIME_CAL,DISPLAY2 ;取时间/日期切换标志位(0表示时间显示,1为日期显示)
;*************显示时钟的时刻****************************
DISPLAY1:
MOV R1,#LEDBUF ;赋时钟显示缓冲区首地址
MOV R7,#8 ;控制显示位数
MOV R2,#10000000B ;定义扫描点初始位置
LOOP:
MOV DPTR,#duanxuan
MOV A,#00H
MOVX @DPTR,A ;先清一次显示
MOV A,@R1
MOV DPTR,#duanxuan
MOVX @DPTR,A
MOV DPTR,#weixuan
MOV A,R2
MOVX @DPTR,A
CALL DELAY ;每一位数码管亮一段时间
RR A
MOV R2,A ;扫描点位置右移一位
INC R1 ;读入下一显示缓冲区的数字
DJNZ R7,LOOP ;LOOP循环执行8次
RET
;**************从右向左移位显示日历************************
DISPLAY2:
MOV R1,#BUFCAL ;赋日历显示缓冲区首地址
MOV DPTR,#duanxuan
MOV A,#00H
MOVX @DPTR,A ;显示清零
MOV A,MINUTE1 ;取当前的分钟值
SUBB A,MINTMP ;减去切换到日历状态时的分钟值
MOV B,#60
MUL AB ;把日历显示的时长换算成秒
ADD A,SECOND1
SUBB A,SECTMP ;得到日历状态下的持续时间
CJNE A,#32,L1 ;当在日历状态下持续32s时,回到时钟状态
CPL TIME_CAL
L1:
MOV A,SECOND1
MOV B,#12 ;除数为日历总共要显示的位数
DIV AB
MOV A,B ;取余得到按下日历显示按键时刻下应该显示日历第几位
ADD A,R1
MOV R1,A ;得到应该显示的缓存地址
MOV R7,#8 ;led屏的个数
MOV A,#80H ;设置压入栈的内容
L2:
PUSH ACC
MOV A,@R1 ;通过R1取出缓冲对应内容
CPL A ;取补码,译码
MOV DPTR,#DUANXUAN
MOVX @DPTR,A
POP ACC ;从栈中取缓存地址
MOV DPTR,#WEIXUAN ;把缓冲内容显示出来
MOVX @DPTR,A
ACALL DELAY ;延长led点亮时间
RR A ;扫描点右移一位
INC R1 ;缓存地址加1
PUSH ACC ;把下一位缓存地址
MOV A,#0
MOV DPTR,#WEIXUAN
MOVX @DPTR,A ;关显示
MOV A,R1
CJNE A,#4CH,L3 ;当最后一位还未显示出来时,跳至L3
MOV R1,#40H ;最后一位显示后,重新到缓存首地址
L3:
POP ACC ;从栈中取出下一轮循环需要的地址值
DJNZ R7,L2 ;L2循环执行8次
RET
;**************延时子程序************************************
DELAY: MOV R3,#45 ;R3寄存器加载45次
DEL1: MOV R4,#20 ;R4寄存器加载20次
DEL2: DJNZ R4,DEL2
DJNZ R3,DEL1 ;总共延时:45*20*2us=1800us
RET
;**************中断0处理子程序*******************************
T0INT:
PUSH PSW ;PSW入栈
PUSH ACC ;ACC入栈
MOV A,C100US ;溢出值的低字节赋给ACC,控制秒增加的速度
JNZ GOON2 ;ACC!=0,跳到GOON2,否则继续
DEC C100US+1
GOON2:
DEC C100US
MOV A,C100US+1
ORL A,C100US
JNZ INTER1
MOV C100US+1,#HIGH(TICK)
MOV C100US,#LOW(TICK)
SJMP INTER2
INTER1: LJMP EXIT
INTER2:
INC second1 ;
MOV A,second1
CJNE A,#60,INTER3
MOV second1,#00H
AJMP INTER4
INTER3: LJMP EXIT
INTER4:
INC minute1
MOV A,minute1
CJNE A,#60,INTER5
MOV minute1,#00H
AJMP INTER6
INTER5: LJMP EXIT
INTER6:
INC hour1
MOV A,hour1
CJNE A,#24,EXIT
MOV hour1,#00H
MOV A,month
CJNE A,#2,NOTFEB ;是否为二月,不是二月转至NOTFEB
ISFEB: NOP
MOV A,YEAR2 ;判断是否闰年(某数末两位数能被4整除,则该数能被4整除)
MOV B, #4
DIV AB
MOV R5,B
CJNE R5,#0,PING ;不是闰年转至PING
RUN: MOV A,date ;是闰年,并取天数(2月份).
CJNE A,#29,DDADD ;29天是否到?未够29天转至DDADD
MOV date,#1 ;够29天,天数置为1
MOV month,#3 ;调整当前月份为3月
SJMP EXIT
PING: MOV A,date ;不是闰年,取天数
CJNE A,#28,DDADD ;28天是否到?未够28天转至DDADD
MOV date,#1 ;够28天,天数置为1
MOV month,#3 ;调整当前月份为3月
SJMP EXIT
NOTFEB: ;不是2月份
CJNE A,#4,YUE1 ;是否为4月份?
AJMP T12
YUE1: CJNE A,#6,YUE2 ;是否为6月份?
AJMP T12
YUE2: CJNE A,#9,YUE3 ;是否为9月份?
AJMP T12
YUE3: CJNE A,#11,T11 ;是否为11月份?
AJMP T12
T11: MOV A,date ;是大月,取天数
CJNE A,#31,DDADD ;31天是否到?未够31天转至DDADD
MOV DATE,#1
AJMP T30 ;够31天转至T30
T12: MOV A,date ;是小月,取天数
CJNE A,#30,DDADD ;30天是否到?未够30天转至DDADD
MOV DATE,#1
T30:
MOV A,month ;取月份数
CJNE A,#12,MMADD ;是否为12月?未够12月转至MMADD
MOV month,#1 ;够12月,月份数置为1
MOV A,year2 ;取年数
CJNE A,#99,YYADD ;是否为99年?未够99年转至YYADD
MOV year2,#00H ;够99年,年数复0
INC year1
SJMP EXIT
YYADD:
INC year2 ;年数低两位加1
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -