⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 new_clock_gai.asm

📁 1
💻 ASM
📖 第 1 页 / 共 2 页
字号:
;************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 + -