End'

	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	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值