RCK BIT P0.5 ;74HC595控制位地址
SCK BIT P0.4
DAT BIT P0.7
RST BIT P0.6
BUZ BIT P1.3 ;蜂鸣器位地址
PATTERN EQU 23H ;工作模式
TD EQU 24H ;进入和退出修改键值
YEA_UNIT EQU 25H ;年份个位
YEA_TEN EQU 26H ;年份十位
YEA_HUNDRED EQU 27H ;年份百位
YEA_THOUSAND EQU 28H ;年份千位
MON_UNIT EQU 29H ;月份百位
MON_TEN EQU 2AH ;月份千位
DAY_UNIT EQU 2BH ;日期个位
DAY_TEN EQU 2CH ;日期十位
MIN_UNIT EQU 2DH ;分钟个位
MIN_TEN EQU 2EH ;分钟十位
HOUR_UNIT EQU 41H ;小时个位
HOUR_TEN EQU 42H ;小时十位
SEC_UNIT EQU 43H ;秒个位
SEC_TEN EQU 44H ;秒十位
VALUE_UINT EQU 2FH ;用于暂时存放修改的日期时间(个位)
VALUE_TEN EQU 30H ;用于暂时存放修改的日期时间(十位)
VALUE_HUNDRED EQU 36H ;用于暂时存放修改的日期时间(百位)
VALUE_THOUSAND EQU 37H ;用于暂时存放修改的日期时间(千位)
BYTE EQU 31H ;传送至74HC595,用以数码管显示的位
TUBE_UNIT EQU 32H ;数码管最右位
TUBE_TEN EQU 33H ;数码管右数第二位
TUBE_HUNDRED EQU 34H ;数码管右数第三位
TUBE_THOUSAND EQU 35H ;数码管右数第四位
CLO_LMIN EQU 45H ;闹钟分个位
CLO_HMIN EQU 46H ;闹钟分十位
CLO_LHOUR EQU 47H ;闹钟时个位
CLO_HHOUR EQU 48H ;闹钟时十位
HMIAO EQU 49H ;定时器秒十位
LMIAO EQU 4AH ;定时器秒个位
HFENMIAO EQU 4BH ;定时器分秒十位
LFENMIAO EQU 4CH ;定时器分秒个位
;中断向量表
ORG 0000H
AJMP MAIN
ORG 0003H
LJMP IINT0 ;外部中断0
ORG 000BH
AJMP EINT0 ;定时器中断0
ORG 0013H
LJMP IINT1 ;外部中断1
ORG 001BH
LJMP EINT1 ;定时器中断1
;主程序
MAIN:
;设定所有模式下的初值
MOV YEA_UNIT,#8 ;日期初值设置为2018年06月17日
MOV YEA_TEN,#1
MOV YEA_HUNDRED,#0
MOV YEA_THOUSAND,#2
MOV MON_TEN,#0
MOV MON_UNIT,#6
MOV DAY_TEN,#1
MOV DAY_UNIT,#7
MOV MIN_UNIT,#0 ;时间初值设置为12:00:00
MOV MIN_TEN,#0
MOV HOUR_UNIT,#2
MOV HOUR_TEN,#1
MOV SEC_UNIT,#0
MOV SEC_TEN,#0
MOV PATTERN,#0
MOV TD,#0
MOV TMOD,#11H
MOV SP,#60H ;堆栈上移
MOV R6,#0
MOV CLO_LMIN,#9 ;闹钟初值设置为24:59
MOV CLO_HMIN,#5
MOV CLO_LHOUR,#4
MOV CLO_HHOUR,#2
MOV HMIAO,#0 ;秒表初值设置为00:00
MOV LMIAO,#0
MOV HFENMIAO,#0
MOV LFENMIAO,#0
SETB BUZ ;打开蜂鸣器
SETB TR0 ;打开定时器0
SETB ET0 ;设置定时器0溢出中断允许位
SETB ET1 ;设置定时器1溢出中断允许位
SETB EX0 ;外部中断0允许
SETB IT0 ;外部中断0边沿触发
SETB EX1 ;外部中断1允许
SETB IT1 ;外部中断1边沿触发
SETB EA ;全局中断允许
SETB PT0 ;设置中断优先级
SETB PX0
SETB PT1
CLR TR1 ;关闭定时器1
CLR RST ;复位端清零
START:
MOV DPTR,#TABLE
;闹钟的报时程序,若定时器0的时间走到了之前的设定,则蜂鸣器响
MOV A,CLO_HHOUR ;逐位比较定时器各位的值
CJNE A,HOUR_TEN, SOUND_EXIT
MOV A,CLO_LHOUR
CJNE A,HOUR_UNIT,SOUND_EXIT
MOV A,CLO_HMIN
CJNE A,MIN_TEN,SOUND_EXIT
MOV A,CLO_LMIN
CJNE A,MIN_UNIT,SOUND_EXIT
LCALL SOUNDLONG ;各位相同,则响铃
MOV CLO_HHOUR,#2 ;恢复初值
MOV CLO_LHOUR,#4
MOV CLO_HMIN,#5
MOV CLO_LMIN,#9
SOUND_EXIT:
MOV A,PATTERN
;各个工作模式的设定
PATTERN0: ;模式0,显示年份
CJNE A,#0,PATTERN1
LCALL SHOW_YEAR
AJMP START
PATTERN1: ;模式1,显示月份和日期
CJNE A,#1,PATTERN2
LCALL SHOW_MON_A_DAY
AJMP START
PATTERN2: ;模式2,显示时和分
CJNE A,#2,PATTERN3
LCALL SHOW_HOU_A_MIN
AJMP START
PATTERN3: ;模式3,显示分和秒
CJNE A,#3,PATTERN4
LCALL SHOW_MIN_A_SEC
AJMP START
PATTERN4: ;模式4,显示闹钟设定值
CJNE A,#4,PATTERN5
LCALL SHOW_ALARM
AJMP START
PATTERN5: ;模式5,秒表(即定时器)
LCALL MIAOBIAO
AJMP START
;不同的显示函数
SHOW_YEAR: ;年份显示函数
MOV VALUE_UINT,YEA_UNIT ;将对应的数值传送到中间量VALUE
MOV VALUE_TEN,YEA_TEN
MOV VALUE_HUNDRED,YEA_HUNDRED
MOV VALUE_THOUSAND,YEA_THOUSAND
LCALL SEG_DISPLAY ;调用数码管显示函数
RET
SHOW_MON_A_DAY: ;月份和日期显示函数
MOV VALUE_UINT,DAY_UNIT
MOV VALUE_TEN,DAY_TEN
MOV VALUE_HUNDRED,MON_UNIT
MOV VALUE_THOUSAND,MON_TEN
LCALL SEG_DISPLAY
RET
SHOW_HOU_A_MIN: ;时分显示函数
MOV VALUE_UINT,MIN_UNIT
MOV VALUE_TEN,MIN_TEN
MOV VALUE_HUNDRED,HOUR_UNIT
MOV VALUE_THOUSAND,HOUR_TEN
LCALL SEG_DISPLAY
RET
SHOW_MIN_A_SEC: ;分秒显示函数
MOV VALUE_UINT,SEC_UNIT
MOV VALUE_TEN,SEC_TEN
MOV VALUE_HUNDRED,MIN_UNIT
MOV VALUE_THOUSAND,MIN_TEN
LCALL SEG_DISPLAY
RET
SHOW_ALARM: ;计时器显示函数
MOV VALUE_UINT,CLO_LMIN
MOV VALUE_TEN,CLO_HMIN
MOV VALUE_HUNDRED,CLO_LHOUR
MOV VALUE_THOUSAND,CLO_HHOUR
LCALL SEG_DISPLAY
RET
MIAOBIAO: ;秒表显示函数
MOV VALUE_UINT,LFENMIAO
MOV VALUE_TEN,HFENMIAO
MOV VALUE_HUNDRED,LMIAO
MOV VALUE_THOUSAND,HMIAO
LCALL SEG_DISPLAY
RET
SEG_DISPLAY: ;数码管显示函数
ACALL CALCULATE ;调用计算函数
DISPLAY1:
MOV A,P0
ANL A,#0F0H
MOV P0,A ;将p0低四位清零,避免数码管显示滞后感
MOV A,TUBE_UNIT ;将各位的值放到A寄存器,以便SEND_BYTE使用
ACALL SEND_BYTE
ORL P0,#08H ;把P0口的P0.3口置1,以点亮最右边的数码管
MOV R7,#17H
LCALL DLY ;调用延时函数,让数码管亮一段时间
MOV A,PATTERN
CJNE A,#5,DIANTUBE_TEN ;如果不是模式5,不显示小数点,跳到显示下一位
MOV A,#10 ;是模式5,查表显示小数点
MOVC A,@A+DPTR
ACALL SEND_BYTE
MOV R7,#17H
LCALL DLY
DIANTUBE_TEN:
MOV A,P0
ANL A,#0F0H ;把P0的低四位全清0
MOV P0,A
MOV R7,#04H ;R7在DLY程序中用到,R7越大,延时越长
LCALL DLY ;让数码管暗一段时间,以避免数码管显示的拖滞感
MOV A, TUBE_TEN ;上面有暗的部分了,故这里不用再写
ACALL SEND_BYTE
ORL P0,#04H ;点亮第三个数码管
MOV R7,#17H
LCALL DLY
MOV A,PATTERN
CJNE A,#5,DIANTUBE_HUNDRED ;判断模式5显示小数点
MOV A,#10
MOVC A,@A+DPTR
ACALL SEND_BYTE
MOV R7,#17H
LCALL DLY
DIANTUBE_HUNDRED:
MOV P0,#00H
MOV R7,#04H
LCALL DLY
MOV A,TUBE_HUNDRED
ACALL SEND_BYTE
ORL P0,#02H ;点亮第二个数码管
MOV R7,#17H
LCALL DLY
MOV A,PATTERN
CJNE A,#1,S2
MOV A,#10
MOVC A,@A+DPTR
ACALL SEND_BYTE
MOV R7,#17H
LCALL DLY
S2: CJNE A,#2,S3
MOV A,#10
MOVC A,@A+DPTR
ACALL SEND_BYTE
MOV R7,#17H
LCALL DLY
S3: CJNE A,#3,S4
MOV A,#10
MOVC A,@A+DPTR
ACALL SEND_BYTE
MOV R7,#17H
LCALL DLY
S4: CJNE A,#4,S5
MOV A,#10
MOVC A,@A+DPTR
ACALL SEND_BYTE
MOV R7,#17H
LCALL DLY
S5: CJNE A,#5,DIANTUBE_THOUSAND
MOV A,#10
MOVC A,@A+DPTR
ACALL SEND_BYTE
MOV R7,#17H
LCALL DLY
DIANTUBE_THOUSAND:
MOV P0,#00H
MOV R7,#04H
LCALL DLY
MOV A,TUBE_THOUSAND ;点亮第一个数码管
ACALL SEND_BYTE
ORL P0,#01H
MOV R7,#17H
LCALL DLY
MOV A,PATTERN
CJNE A,#5,DIAN
MOV A,#10
MOVC A,@A+DPTR
ACALL SEND_BYTE
MOV R7,#17H
LCALL DLY
DIAN:
RET
;通过串行输入的方式向74hc595发送一个字节数据
;并将数据输出到数码管的段码管脚
;对字节进行输入锁存
SEND_BYTE:
MOV BYTE,A
MOV A,#80H
MOV R7,#9
JUDGE1:
DJNZ R7,SEND_LOOP ;当R7不为0时,进入循环
CLR RCK
SETB RCK
CLR RCK
RET
SEND_LOOP:
RL A
MOV B,A
ANL A,BYTE
JNZ SET_DAT ;当不为0时,跳去设置dat为1
CLR DAT
MOV A,B
SCK_1:
CLR SCK
SETB SCK
CLR SCK
AJMP JUDGE1
SET_DAT:
SETB DAT
MOV A,B
AJMP SCK_1
;此处为延时子程序
DLY:
PUSH 04H
MOV R4,200
PUSH 05H
DLY0:
MOV A,R7
MOV R5,A
DLY1:
DJNZ R5,DLY1
DJNZ R4,DLY0
POP 05H
POP 04H
RET
CALCULATE:
;该函数负责将存放在VALUE里的需要传送进数码管显示出来的数值,通过查表转换成适应数码管显示的数值。
MOV A,VALUE_UINT ;查表
MOVC A,@A+DPTR
MOV TUBE_UNIT,A ;数码管要显示的数值
MOV A,VALUE_TEN
MOVC A,@A+DPTR
MOV TUBE_TEN,A
MOV A,VALUE_HUNDRED
MOVC A,@A+DPTR
MOV TUBE_HUNDRED,A
MOV A,VALUE_THOUSAND
MOVC A,@A+DPTR
MOV TUBE_THOUSAND,A
RET
;外部中断0子程序
IINT0:
PUSH ACC
MOV R7,#20
ACALL DLY
JB P3.2,INT0END ;以上几步为消除按键抖动
MOV A,TD ;检测修改标志位
CJNE A,#1,INT0_1 ;若不在修改状态,则正常模式切换
MOV TD,#2 ;若在修改状态
AJMP INT0END ;则按下INT0不作任何反应
INT0_1:
INC PATTERN
MOV A,PATTERN
CJNE A,#6,INT0END ;若已经为最后一个模式,则将模式清零
MOV PATTERN,#0
INT0END:
POP ACC
RETI
;定时器0中断
EINT0:
PUSH ACC
CLR TF0 ;消除溢出位
MOV TH0,#0D8H ;给定时器设定初值,使其每20ms溢
MOV TL0,#0F0H ;出一次
INC R6
CJNE R6,#50,FIR_EINT0END ;溢出50次达一秒
MOV R6,#0
INC SEC_UNIT ;对各个时间标志位进行修改
MOV A,SEC_UNIT ;修改秒的个位
CJNE A,#10,FIR_EINT0END ;判断是否有进位
MOV SEC_UNIT,#0 ;进位处理
INC SEC_TEN
MOV A,SEC_TEN ;判断下一位
CJNE A,#6,FIR_EINT0END ;秒的十位最多到6
MOV SEC_TEN,#0
INC MIN_UNIT
MOV A,MIN_UNIT
CJNE A,#10,SEC_EINT0END
MOV MIN_UNIT,#0
INC MIN_TEN
MOV A,MIN_TEN
CJNE A,#6,SEC_EINT0END
MOV MIN_TEN,#0
INC HOUR_UNIT
MOV A,HOUR_UNIT
CJNE A,#10,INC_HOUR ;先判断是否有向十位进位,
MOV HOUR_UNIT,#0 ;有的话做进位处理
INC HOUR_TEN
INC_HOUR:
MOV A,HOUR_TEN
CJNE A,#2,SEC_EINT0END ;没有的话,判断是否有向“天”进位
MOV A,HOUR_UNIT
CJNE A,#4,SEC_EINT0END ;判断是否等于24时
MOV HOUR_UNIT,#0
MOV HOUR_TEN,#0
INC DAY_UNIT
;向“Day”进位后注意有大月小月,2月的区别,进而有平年闰年的区分
MOV A,MON_UNIT
CJNE A,#2,NOT_FEB ;先判断是否为2月
MOV A,MON_TEN
CJNE A,#0,BIG_MON ;12月,转大月
MOV A,DAY_TEN ;2月
CJNE A,#2,NINC_FEB
MOV A,DAY_UNIT
CJNE A,#9,CHN ;判断进位后是否为29号
;判断是否为闰年
MOV A,YEA_UNIT
MOV B,#10
MUL AB
ADD A,YEA_UNIT
CJNE A,#0,NORM_YEAR ;判断是否为整百年
;若为整百年,取高两位,看是否能被4整除
MOV A,YEA_THOUSAND
MOV B,#10
MUL AB
ADD A,YEA_HUNDRED
;不是整百年,取低两位,看是否能被4整除(因为百位以上部分一定可以被4整除)
NORM_YEAR:
MOV B,#4
DIV AB
MOV A,B
CJNE A,#0,INC_FEB ;平年,进位
LJMP EINT0END ;闰年,返回
FIR_EINT0END:
LJMP SEC_EINT0END ;为上面的JMP EINT0END做跳板
CHN:
CJNE A,#10,SEC_EINT0END ;若进位后不为29号,则不用考虑向“月”进位问题
INC_FEB:
MOV DAY_UNIT,#1 ;向月进位
MOV DAY_TEN,#0
INC MON_UNIT
LJMP EINT0END
NINC_FEB:
MOV A,DAY_UNIT ;对于2月20号以前的情况(不用考虑向月进位)
CJNE A,#10,SEC_EINT0END
MOV DAY_UNIT,#0
INC DAY_TEN
LJMP EINT0END
SEC_EINT0END:
LJMP EINT0END ;为上面的FIR_EINT0END做跳板
;下面做除2月以外,大小月的判别
NOT_FEB:
MOV A,MON_UNIT
CJNE A,#1,NOT_JAN
MOV A,MON_TEN
CJNE A,#1,BIG_MON ;若为1月,则跳到大月的处理
SJMP SML_MON ;11月,跳到小月处理
NOT_JAN:
MOV A,MON_UNIT
CJNE A,#3,NOT_MAR ;3月判断
NOT_MAR:
CJNE A,#5,NOT_MAY
NOT_MAY:
CJNE A,#7,NOT_JUL
NOT_JUL:
CJNE A,#8,NOT_AUG
NOT_AUG:
CJNE A,#0,SML_MON ;如果个位接受进位后不是10,则不是10月
BIG_MON:
MOV A,DAY_UNIT
CJNE A,#10,CHECK_BMON ;检查是否有向十位进位
SJMP INC_DAY_TEN
;11月和12月之前检测过
SML_MON: ;小月是除了1,2,3,5,7,8,10,12以外的月份
MOV A,DAY_UNIT
CJNE A,#10,CHECK_SMON
SJMP INC_DAY_TEN
CHECK_SMON:
CJNE A,#1,EINT0END ;查看日期的个位是否为1
INC A
CHECK_BMON:
CJNE A,#2,EINT0END ;检查是否为31号
MOV A,DAY_TEN
CJNE A,#3,EINT0END
MOV DAY_UNIT,#1 ;向“月”进位
MOV DAY_TEN,#0
SJMP INC_MON
INC_DAY_TEN: ;如果是有个位向十位的进位,则一定不用向“月”进位
MOV DAY_UNIT,#0 ;只有“日”在进位后为31或32后,才需要向“月”进位
INC DAY_TEN
LJMP EINT0END
INC_MON:
INC MON_UNIT
CJNE A,#3,NOT_DEC ;查看是否是12月
MOV A,MON_TEN
CJNE A,#1,NOT_DEC
MOV MON_UNIT,#1 ;是12月
MOV MON_TEN,#0
SJMP INC_YEAR ;向年进位
NOT_DEC: ;不是12月,则不用考虑是否向年进位
CJNE A,#10,EINT0END
MOV MON_UNIT,#0
INC MON_TEN
LJMP EINT0END
INC_YEAR: ;年份各位之间的进位
INC YEA_UNIT
MOV A,YEA_UNIT
CJNE A,#10,EINT0END
MOV YEA_UNIT,#0
INC YEA_TEN
MOV A,YEA_TEN
CJNE A,#10,EINT0END
MOV YEA_TEN,#0
INC YEA_HUNDRED
MOV A,YEA_HUNDRED
CJNE A,#10,EINT0END
MOV YEA_HUNDRED,#0
INC YEA_THOUSAND
MOV A,YEA_THOUSAND
CJNE A,#10,EINT0END
MOV YEA_THOUSAND,#0
EINT0END:
POP ACC
RETI
;外部中断1,修改数值的入口
IINT1:
MOV A,PATTERN
CJNE A,#5,SS
PUSH ACC ;若当前模式为运动秒表
MOV R7,#20 ;则根据TD值判断运动秒表状态
LCALL DLY ;并作相应操作和修改TD值
JB P3.3,INT0R1 ;判断按键INT1是否按下
MOV A,TD
INC A
MOV TD,A
CJNE A,#1,STOP ;若TD为1,运动秒表等待启动
LCALL SOUNDSHORT ;蜂鸣器发声同时启动运动秒表开始计时
SETB TR1
AJMP INT0R1
STOP:
CJNE A,#2,QINGLING ;若TD为2,运动秒表中止,显示最后数值
CLR TR1 ;停止定时器1
AJMP INT0R1
QINGLING: ;若TD为3,运动秒表等待清零
MOV HMIAO,#0 ;运动秒表置位
MOV LMIAO,#0
MOV HFENMIAO,#0
MOV LFENMIAO,#0
MOV TD,#0
LCALL SOUNDSHORT ;秒表置位且蜂鸣器发声
AJMP INT0R1
;若是模式0~4,按下按键INT1后就可以修改数码管各位上的数值
SS:
PUSH ACC
MOV R7,#20
LCALL DLY ;延时程序
JB P3.3,INT0R1 ;判断按键INT1是否按下
MOV A,TD ;根据TD值判断进入或退出修改状态
INC A
MOV TD,A
CX0:
MOV A,TD
CJNE A,#1, INT0R0 ;TD为1时,进入修改
LCALL KEYSCAN ;调用键盘扫描函数
NSTART:
MOV DPTR,#TABLE
MOV A,PATTERN ;判断修改时的模式
;以下分别为0~4模式下的修改
NPATTERN0: ;模式0显示四位年份
CJNE A,#0,NPATTERN1 ;判断是否为模式 0,否则跳模式 1
LCALL SHOW_YEAR ;不断调用显示函数
AJMP CX0
NPATTERN1: ;模式1显示月份日期
CJNE A,#1,NPATTERN2 ;判断是否为模式 1,否则跳模式2
LCALL SHOW_MON_A_DAY
AJMP CX0
NPATTERN2: ;模式2显示时分
CJNE A,#2,NPATTERN3 ;判断是否为模式2,否则跳模式3
LCALL SHOW_HOU_A_MIN
AJMP CX0
NPATTERN3:
CJNE A,#3,NPATTERN4 ;判断是否为模式3,否则跳模式4
LCALL SHOW_MIN_A_SEC ;模式3显示分秒
AJMP CX0
NPATTERN4:
LCALL SHOW_ALARM ;模式4显示闹钟
AJMP CX0
INT0R0:
CJNE A,#2,INT0R1
MOV TD,#0
INT0R1:
POP ACC
RETI
;键盘扫描函数,判断哪一个按键按下,修改对应的数码管显示值
KEYSCAN:
MOV P2,#0FH ;列线送出全0
MOV A,P2 ;读入行线值
ANL A,#0FH ;比较原状态
CJNE A,#0FH,KK1 ;判断是否有按键按下
MOV R3,#0
AJMP KEYSCANEND
KK1:
MOV R7,#20H
LCALL DLY ;延时去抖
MOV P2,#0FH ;列线再送出全0
MOV B,P2 ;读入P2口值
MOV A,P2
ANL A,#0FH ;比较原状态,确认按键已按下
CJNE A,#0FH,KK2
AJMP KEYSCANEND
KK2:
NOP
MOV R4,B ;记下行口线值
MOV P2,#0F0H ;行线送出全0
MOV A,P2 ;读入列线值
ANL A,#0F0H ;比较原状态
CJNE A,#0F0H,KK3 ;判断是否有按键按下
AJMP KEYSCANEND
KK3:
NOP
MOV P2,#0F0H ;列线再送出全0
MOV A,P2 ;读入P2口的值
MOV B,P2
ANL A,#0F0H ;比较原状态
CJNE A,#0F0H,KK4 ;确认按键已按下
AJMP KEYSCANEND
KK4:
NOP
MOV R5,B
MOV A,R4
ORL A ,R5
MOV R5,A ;R5存放检测出的按键的序号
AJMP JIANCE
JIANCE:
MOV P2,#0F0H
MOV A,P2 ;读入列线值
ANL A,#0F0H ;比较原状态
CJNE A,#0F0H,JIANCE ;按键松开才进入修改函数,
;否则一直检测按键
AJMP MODIFY0
KEYSCANEND:
RET ;按键扫描返回
;以下为不同模式下的修改函数
MODIFY0:
MOV A ,PATTERN
CJNE A,#0H,MODIFY1 ;判断是否为模式0,否则跳模式1
MOV A,R5
CJNE A ,#77H,AA01 ;检测到(1,1)键按下,修改年千位
MOV A, YEA_THOUSAND
INC A ;每按键一次,数值增1
CJNE A,#10,J00 ;进位,清零
MOV YEA_THOUSAND,#0
AJMP KEYSCANEND
J00:
INC YEA_THOUSAND ;没有进位,直接加1
AJMP KEYSCANEND
AA01:
CJNE A ,#7BH,AA02 ;检测到(1,2)键按下,修改年百位
MOV A, YEA_HUNDRED
INC A ;每按键一次,数值增1
CJNE A,#10,J01 ;进位,清零
MOV YEA_HUNDRED,#0
AJMP KEYSCANEND
J01:
INC YEA_HUNDRED ;没有进位,直接加1
AJMP KEYSCANEND
AA02:
CJNE A ,#7DH,AA03 ;检测到(1,3)键按下,修改年十位
MOV A, YEA_TEN
INC A ;每按键一次,数值增1
CJNE A,#10,J02 ;进位,清零
MOV YEA_TEN,#0
AJMP KEYSCANEND
J02:
INC YEA_TEN ;没有进位,直接加1
AJMP KEYSCANEND
AA03:
CJNE A ,#7EH,KEYSCANEND ;检测到(1,4)键按下,修改年个位
MOV A, YEA_UNIT
INC A ;每按键一次,数值增1
CJNE A,#10,J03 ;进位,清零
MOV YEA_UNIT,#0
AJMP KEYSCANEND
J03:
INC YEA_UNIT ;没有进位,直接加1
AJMP KEYSCANEND
MODIFY1:
MOV A ,PATTERN
CJNE A,#1,MODIFY2 ;判断是否为模式1,否则跳模式2
MOV A,R5
CJNE A ,#77H,A11 ;检测到(1,1)键按下,修改月十位
MOV A, MON_TEN
INC A
CJNE A,#2,J11 ;月份高位最高为1
MOV MON_TEN,#0
AJMP KEYSCANEND ;没有进位,直接加1
J11:
INC MON_TEN
AJMP KEYSCANEND
A11:
CJNE A ,#7BH,A12 ;检测到(1,2)键按下,修改月个位
MOV A, MON_UNIT
INC A
CJNE A,#10,J12 ;进位,清零
MOV MON_UNIT,#0
AJMP KEYSCANEND
J12:
INC MON_UNIT ;没有进位,直接加1
S: AJMP KEYSCANEND
A12:
CJNE A ,#7DH,A13 ;检测到(1,3)键按下,修改日十位
MOV A, DAY_TEN
INC A
CJNE A,#4,J13 ;日期高位最高为3
MOV DAY_TEN,#0
AJMP KEYSCANEND
J13:
INC DAY_TEN ;没有进位,直接加1
AJMP KEYSCANEND
A13:
CJNE A,#7EH,S ;检测到(1,4)键按下,修改日个位
MOV A, DAY_UNIT
INC A
CJNE A,#10,J14 ;进位,清零
MOV DAY_UNIT,#0
AJMP KEYSCANEND
J14:
INC DAY_UNIT ;没有进位,直接加1
AJMP KEYSCANEND
MODIFY2:
MOV A ,PATTERN
CJNE A,#2,MODIFY3 ;判断是否为模式2,否则跳模式3
MOV A,R5
CJNE A ,#77H,A21 ;检测到(1,1)键按下,修改时十位
MOV A, HOUR_TEN
INC A
CJNE A,#3,J21 ;小时的高位不超过2
MOV HOUR_TEN,#0
AJMP KEYSCANEND
J21:
INC HOUR_TEN ;没有进位,直接加1
AJMP KEYSCANEND
A21:
CJNE A ,#7BH,A22 ;检测到(1,2)键按下,修改时个位
MOV A, HOUR_UNIT
INC A
CJNE A,#10,J22
MOV HOUR_UNIT,#0 ;进位,清零
AJMP KEYSCANEND
J22:
INC HOUR_UNIT ;没有进位,直接加1
AJMP KEYSCANEND
A22:
CJNE A ,#7DH,A23 ;检测到(1,3)键按下,修改分十位
MOV A, MIN_TEN
INC A
CJNE A,#6,J23 ;分钟最高位为5
MOV MIN_TEN,#0
AJMP KEYSCANEND
J23:
INC MIN_TEN ;没有进位,直接加1
AJMP KEYSCANEND
A23:
CJNE A,#7EH,S ;检测到(1,4)键按下,修改分个位
MOV A, MIN_UNIT
INC A
CJNE A,#10,J24
MOV MIN_UNIT,#0
AJMP KEYSCANEND
J24:
INC MIN_UNIT ;没有进位,直接加1
AJMP KEYSCANEND
MODIFY3:
MOV A ,PATTERN ;判断是否为模式3,否则跳模式4
CJNE A,#4,S
MOV A,R5
CJNE A ,#77H,A31 ;检测到(1,1)键按下,修改闹钟时十位
MOV A, CLO_HHOUR
INC A
CJNE A,#3,J31 ;小时十位最高为2
MOV CLO_HHOUR,#0
AJMP KEYSCANEND
J31:
INC CLO_HHOUR ;没有进位,直接加1
AJMP KEYSCANEND
A31:
CJNE A ,#7BH,A32 ;检测到(1,2)键按下,修改闹钟时个位
MOV A, CLO_LHOUR
INC A
MOV B,CLO_HHOUR
XCH A,B
CJNE A,#2,CJL
XCH A,B
CJNE A,#5,J32 ;进位,清零
MOV CLO_LHOUR,#0
AJMP KEYSCANEND
CJL:
XCH A,B
CJNE A,#10,J32
MOV CLO_LHOUR,#0
AJMP KEYSCANEND
J32:
INC CLO_LHOUR ;没有进位,直接加1
AJMP KEYSCANEND
A32:
CJNE A ,#7DH,A33 ;检测到(1,3)键按下,修改闹钟分十位
MOV A, CLO_HMIN
INC A
CJNE A,#6,J33 ;闹钟分钟十位最高为5
MOV CLO_HMIN,#0
AJMP KEYSCANEND
J33:
INC CLO_HMIN ;没有进位,直接加1
SSS:
AJMP KEYSCANEND
A33:
CJNE A,#7EH,SSS ;检测到(1,4)键按下,修改闹钟分十位
MOV A, CLO_LMIN
INC A
CJNE A,#10,J34 ;进位,清零
MOV CLO_LMIN,#0
AJMP KEYSCANEND
J34: ;没有进位,直接加1
INC CLO_LMIN
AJMP KEYSCANEND
;定时器1中断子程序,用于定时器(即秒表)的计时
EINT1:
PUSH ACC
CLR TF1 ;清中断标志
MOV TH1,#0ECH ;设定初值,保证每1分秒溢出一次
MOV TL1,#078H ;在晶振频率为6MHz的条件下 ,设定时器1初值为EC78H=60536
INC LFENMIAO
MOV A,LFENMIAO
CJNE A,#10,EINT1END ;进位
MOV LFENMIAO,#0
INC HFENMIAO
MOV A,HFENMIAO
CJNE A,#10,EINT1END
MOV HFENMIAO,#0
INC LMIAO
MOV A,LMIAO
CJNE A,#10,EINT1END ;进位
MOV LMIAO,#0
INC HMIAO
MOV A,HMIAO
CJNE A,#6,EINT1END ;最高位不超过5
MOV HMIAO,#0
EINT1END:
POP ACC
RETI
;蜂鸣器发声子程序
SOUNDSHORT:
CLR BUZ ;下降沿触发蜂鸣器
MOV R7,#0FH
LCALL DLY
SETB BUZ
MOV R7,#02H
LCALL DLY ;结合延时程序
RET
SOUNDLONG:
PUSH 05H ;堆栈,保护现场
MOV R5,#25
CLK:
CLR BUZ
MOV R7,#0FFH
LCALL DLY
SETB BUZ
MOV R7,#0FFH
LCALL DLY
DJNZ R5,CLK ;延时嵌套,设置合适的闹钟响铃时间
POP 05H
RET
;数码管字符转换表
TABLE:
DB 0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6,0x01
END