基于51+DS1302智能时钟设计

项目要求:

智能时钟设计(LCD 显示):利用 DS1302 芯片,设计一个智能电子钟,要求:

  1. 可显示年月日,星期及时分秒
  2. 双行字符型 Lcd 显示(一行显示年月日星期,一行显示时分秒)。
  3. 设置四个独立按键 K1~K4,其功能分别为:
    K1:进入/退出闹钟设置;
    K2:闹钟路数选择;
    K3:闹钟小时设置;
    K4:闹钟分钟设置。
    只有在进入闹钟设置界面后,按键 2~4 才有效。此时 LCD 显示:
    第一行: BELL TIME
    第二行: 路数(显示 1 或 2) 小时:分钟
  4. 允许设置两路闹钟。默认无闹钟
  5. 闹钟所设时间到时,从蜂鸣器输出 5s 的闹铃。

总体设计:

  1. 实现DS1302的初始化:能够写入RAM值来初始化计时时间,能够从RAM中读取时分秒年月日星期。
  2. 实现LCD1602初始化:能够在某一位置显示相应字符。
  3. 实现计时模式和闹钟模式切换:用单刀双置开关实现,0-表示计时模式,1-表示闹钟模式,在while循环中进行判断即可。
  4. 实现闹钟路数选择:依旧采用单刀双置双置开关,0-表示闹钟路数1,1-表示闹钟路数2。闹钟初值设置成0XFF,表示无闹钟(时最多24,分和秒最多60)。
  5. 实现闹钟设定:利用两个下降沿按键中断,每按下一次小时和分钟就加1,到60就清零。
  6. 闹钟蜂鸣器:每刷新一次时间就与设定好的闹钟时间进行对比即可。

电路设计:

在这里插入图片描述

程序设计:

;******************* 宏定义 ************************

	LCDIO   EQU  P0	   ;LCD数据总线连接P0口
	RS      EQU  P2.0  ;Register select 连接P2.0
	RW      EQU  P2.1  ;Read/Write 连接P2.1
	E       EQU  P2.2  ;Enable 连接P2.2
	BF      EQU  P0.7  ;LCD的忙标志位
	
	CLOCKIO EQU  P1.0  ;DS1302数据IO
	SCLK    EQU  P1.1  ;DS1302时钟端口
	RST     EQU  P1.2  ;DS1302复位端口
	
	SECOND  EQU  60H   ;秒地址
	MINUTE  EQU  61H   ;地址
	HOUR    EQU  62H   ;时地址
	YEAR    EQU  63H   ;日地址
	MOUTH   EQU  64H   ;月地址
	WEEK    EQU  65H   ;周地址
	DAY     EQU  66H   ;年地址

	BUZZER  EQU  P2.7  ;蜂鸣器

	CLOCK_STATUS        EQU  P2.3  ;闹钟状态,1--退出闹钟状态,0--进入闹钟状态
	CLOCK_LINE_STATUS	EQU  P2.6  ;闹钟路数,1--路数10--路数2 
;***************************************************
	
	ORG 0000H
	AJMP MAIN
	ORG 0003H
	LJMP INT0_INTERRUPT
	ORG 0013H
	LJMP INT1_INTERRUPT

	ORG 0100H
;******************** 主函数 ************************

MAIN:
 	NOP
	LCALL CLOCK_INIT ;调用DS1602初始化函数
	LCALL LCD_INIT	 ;调用LCD1602初始化函数
	LCALL EXTI_INTER_INIT ;调用中断初始化函数

	SETB RS1
	SETB RS0
	MOV R0, #0FFH
	MOV R1, #0FFH
	MOV R2, #0FFH
	MOV R3, #0FFH
	CLR RS1
	CLR RS0

	CLR BUZZER

LOOP:
	LCALL GET_TIME	 ;调用获取时间函数,存储起始地址60H
	LCALL BUZ_1
	LCALL BUZ_2
	LCALL SHOW_TIME	 ;调用LCD显示时间函数
	MOV DPTR, #385   ;调用延时函数
	MOV A, #1
	LCALL T0_DELAY

	JB CLOCK_STATUS,LOOP
	LCALL CLOCK_MODE
	AJMP LOOP 
;***************************************************

;**************** LCD1602初始化 *********************
LCD_INIT:
	MOV A, #01H  ;写入清屏命令 
	ACALL W_CMD
	MOV A, #38H  ;设置显示模式16*2显示
	ACALL W_CMD
	MOV A, #0CH  ;设置屏幕显示,无光标
	ACALL W_CMD
	MOV A, #14H  ;设置右移
	ACALL W_CMD
	RET
;***************************************************
	  
;**************** LCD1602忙标志 *********************
      	  
WAIT_BUSY: 
	MOV LCDIO,#0FFH
	CLR RS
	SETB RW
	CLR E
	NOP
	SETB E
	JB BF, WAIT_BUSY
	RET
;***************************************************

;*************** LCD1602写入命令函数 ****************

W_CMD:
	LCALL WAIT_BUSY 
	MOV LCDIO, A ;把命令写到IO口处
	CLR RS	   ;0,选择命令寄存器
	CLR RW
	SETB E	   ;模拟下降沿,命令写入LCD中
	NOP
	CLR E
	RET
;***************************************************

;*************** LCD1602写入数据函数 ****************

W_DAT:
	LCALL WAIT_BUSY 
	MOV LCDIO, A
	SETB RS	   ;1,选择数据寄存器
	CLR RW	   ;0,为写入,置1,为读取
	SETB E	   ;模拟下降沿
	NOP 
	CLR E
	RET
;***************************************************

;************** DS1302写入字节函数 ******************
;将B写入DS1302  --传入参数B   --无传出参数

INPUT: 
	MOV R4, #8
INPUT_Rotate:
	MOV A, B
	RRC A
	MOV B, A
	MOV CLOCKIO, C
	
	SETB SCLK		 ;SCLK下降沿写入字节
	NOP
	CLR SCLK
	
	DJNZ R4, INPUT_Rotate
	RET
;***************************************************

;************** DS1302读出字节函数 ******************
;将DS1302数据读出到A

OUTPUT:
	MOV R4, #8
OUTPUT_Rotate:		
	MOV C, CLOCKIO
	RRC A
	
	SETB SCLK		 ;SCLK下降沿读出字节
	NOP
	CLR SCLK
	
	DJNZ R4, OUTPUT_Rotate
	RET 
;***************************************************

;************* 定义DS1302启示计时时间 ***************
;写入 201219日 周6 131313秒

TIME_SET:	 
	 MOV SECOND,  #13H	  ;秒
	 MOV MINUTE,  #13H	  ;分
	 MOV HOUR,    #13H	  ;时
	 MOV DAY,     #19H	  ;日
	 MOV MOUTH,   #12H	  ;月
	 MOV WEEK,    #06H	  ;周
	 MOV YEAR,    #20H	  ;年
	 RET
;***************************************************

;****************** 初始化DS1302 ********************
CLOCK_INIT:
;32字节存储从DS1302读取的数据	
	MOV R0, #60H  ;60H开始,依次增加
	MOV R1, #07H  ;作为索引,索引32个连续字节
	MOV A, #00H	  ;A起到中间变量,暂时存储00H
CLR_BUF:
	MOV @R0, A	  ;间接寻址,把00H依次写入60H开始的32个字节中
	INC R0		  ;地址增1
	DJNZ R1, CLR_BUF   ;跳转,依次赋值
;写入命令字进行初始化	
	CLR RST		   ;模拟RST上升沿,启动数据传输(说明启动传输不是代表传输,还需要SCLK的上升沿
	CLR SCLK
	NOP									 ; 还需要SCLK的上升沿才能把数据按位依次传输过去)
	SETB RST
	
	MOV B, #8EH    ;命令字,写保护寄存器,此时不能对寄存器进行写操作了
	LCALL INPUT	 ;把命令字写如DS1302
	
	MOV B, #00H    ;WP清零,现在允许外部写入DS1302中数据
	LCALL INPUT	 ;把命令字写如DS1302
	
	LCALL TIME_SET	;设定初始化时间
	
	MOV R0, #SECOND	;把在RAM中地址为60H(存储数据为秒)数据给R0
	MOV R7, #7		;索引,秒、分......送七次
	MOV R1, #80H	;命令字。表示写入秒数据,当R2依次增二,表明开始进行写如分,时等数据.(这是规定,写如数据前先给命令字)

WRITE:
	CLR RST		 ;模拟RST上升沿,启动数据传输
	CLR SCLK
	NOP  
	SETB RST
	
	MOV B, R1		;将命令字80H写入DS1302
	LCALL INPUT
	MOV A,@R0	    ;R0存储的秒地址,间接寻址将数据给A
	MOV B, A		;必须把数据传给B,因为写入函数(INPUT)传入参数是B
	LCALL INPUT
	INC R0		;R0增1表示由秒向分过渡,或者由分向时过渡,依次类推
	INC R1		;R1增2,表示由秒向分的命令字过渡,或者由分向时的命令字过渡,依次类推
	INC R1
	
	SETB SCLK
	CLR RST
	DJNZ R7, WRITE  ;循环7次,将秒,分,时,星期,天,月,年都写入
	
	MOV B, #8EH    ;写保护寄存器
	LCALL INPUT
	
	MOV B, #80H    ;WP置1,写保护
	LCALL INPUT		
	
	SETB SCLK
	CLR RST
;***************************************************

;***************** 获取时间函数 *********************

GET_TIME:
	MOV R0, #SECOND		;把秒的地址给R0,以便依次读出数据
	MOV R7, #7		    ;用来循环
	MOV R1, #81H		;命令字。表示读出秒数据,当R2依次增二,表明开始进行写如分,时等数据.
GET_TIME_Rotato:
	CLR RST				;模拟RST上升沿,表示启动数据传输
	CLR SCLK
	NOP
	SETB RST

	MOV B, R1		 	;把命令字给B,由B写如DS1302中
	LCALL INPUT			;写入命令字
	LCALL OUTPUT		;读出数据,读出的数据是保存到A中的
	MOV @R0, A			;现在将读出来的值放到秒或者分等的地址中
	INC R0				;地址加1,从而过渡到分,时等
	INC R1				;命令字需要加2才能进行读取
	INC R1
	SETB SCLK
	CLR RST
	DJNZ R7, GET_TIME_Rotato	;循环读取
	RET	   
;***************************************************

;***************** 显示时间函数 *********************

SHOW_TIME:
;显示小时
	MOV A, #84H
	LCALL W_CMD
	MOV A, HOUR
	PUSH Acc
	SWAP A
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT  ;显示小时高位
	
	MOV A, #85H
	LCALL W_CMD
	
	POP Acc
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示小时低位

;显示符号-
	MOV A, #86H
	LCALL W_CMD
	MOV A, #2DH
	LCALL W_DAT

;显示分钟
	MOV A, #87H
	LCALL W_CMD
	MOV A, MINUTE
	PUSH Acc
	SWAP A
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT  ;显示分钟高位
	
	MOV A, #88H
	LCALL W_CMD
	
	POP Acc
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示分钟低位

;显示符号-
	MOV A, #89H
	LCALL W_CMD
	MOV A, #2DH
	LCALL W_DAT

;显示秒
	MOV A, #8AH
	LCALL W_CMD
	MOV A, SECOND
	PUSH Acc
	SWAP A
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT  ;显示秒高位
	
	MOV A, #8BH
	LCALL W_CMD
	
	POP Acc
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示秒低位

;显示年
	MOV A, #0C3H
	LCALL W_CMD
	MOV A, YEAR
	PUSH Acc
	SWAP A
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT  ;显示年高位
	
	MOV A, #0C4H
	LCALL W_CMD
	
	POP Acc
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示年低位

;显示符号-
	MOV A, #0C5H
	LCALL W_CMD
	MOV A, #2DH
	LCALL W_DAT

;显示月
	MOV A, #0C6H
	LCALL W_CMD
	MOV A, MOUTH
	PUSH Acc
	SWAP A
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT  ;显示月高位
	
	MOV A, #0C7H
	LCALL W_CMD
	
	POP Acc
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示月低位

;显示符号-
	MOV A, #0C8H
	LCALL W_CMD
	MOV A, #2DH
	LCALL W_DAT

;显示日
	MOV A, #0C9H
	LCALL W_CMD
	MOV A, DAY
	PUSH Acc
	SWAP A
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT  ;显示日高位
	
	MOV A, #0CAH
	LCALL W_CMD
	
	POP Acc
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示日低位

;显示符号-
	MOV A, #0CBH
	LCALL W_CMD
	MOV A, #2DH
	LCALL W_DAT

;显示周
	MOV A, #0CCH
	LCALL W_CMD
	MOV A, WEEK
	
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示周
	
	RET
;****************** T0延时函数 *******************

T0_DELAY:

	PUSH Acc
	LCALL T0_WAIT
	POP Acc
	DEC A
	JNZ T0_DELAY
	RET
T0_WAIT:
	XRL DPL, #0FFH
	XRL DPH, #0FFH
	INC DPTR
T0_1:
	MOV TL0, #09CH
	MOV TH0, #0FFH
	MOV TMOD, #1
	SETB TCON.4
T0_2:


	JNB TCON.5, T0_2
	CLR TCON.4
	CLR TCON.5
	INC DPTR
	MOV A, DPL
	MOV A, DPH
	JNZ T0_1
	RET
;***************************************************


;********************** 闹钟模式 *********************
;1--退出闹钟模式, 0--进入闹钟模式

CLOCK_MODE:
	LCALL LCD_INIT

CLOCK_MODE_ROTATE:
	
	JB CLOCK_STATUS, EXIT
	LJMP CLOCK_SHOW 
AA:
	MOV DPTR, #390   ;调用延时函数
	MOV A, #1
	LCALL T0_DELAY
	NOP

	LJMP CLOCK_MODE_ROTATE
EXIT:
	MOV A, #01H  ;写入清屏命令 
	ACALL W_CMD
	RET
;***************************************************

;********************** 闹钟显示 *********************
CLOCK_SHOW:

;第一行显示	
	MOV R0, #83H	  ;判断写入位置
	MOV DPTR, #TAB1	  ;初始地址
    MOV R1, #00H								   ;这个地方又错误
CLOK_SHOW_ROTATE:
	MOV A, R0
	LCALL W_CMD
	MOV A, R1
	MOVC A, @A+DPTR
	LCALL W_DAT

	INC R0
	INC R1
	CJNE R1, #10, CLOK_SHOW_ROTATE
	NOP
	   	
;第二行显示
;闹钟路数,1--路数10--路数2
	JB CLOCK_LINE_STATUS,CLOCK_LINE_1
	AJMP CLOCK_LINE_2

CLOCK_LINE_1:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
	NOP

	LJMP CLOCK_LINE_1_SHOW
BB:
	NOP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	AJMP CLOCK_LINE_LOOP	 
CLOCK_LINE_2:
	
	LJMP CLOCK_LINE_2_SHOW
CC:
CLOCK_LINE_LOOP:
	LJMP AA

;***************************************************

;***************闹钟路数1显示函数********************

 CLOCK_LINE_1_SHOW:

	NOP
	MOV A, #0C4H	  ;判断写入位置
	NOP
	LCALL W_CMD
	NOP

	MOV A, #1
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示闹钟路数
;显示小时
	MOV A, #0C7H
	LCALL W_CMD

	SETB RS1
	SETB RS0
	MOV A, R0
	CLR RS1
	CLR RS0

	PUSH Acc
	SWAP A
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT  ;显示小时高位
	
	MOV A, #0C8H
	LCALL W_CMD
	
	POP Acc
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示小时低位

;显示符号-
	MOV A, #0C9H
	LCALL W_CMD
	MOV A, #3AH
	LCALL W_DAT

;显示分钟
	MOV A, #0CAH
	LCALL W_CMD

	SETB RS1
	SETB RS0
	MOV A, R1
	CLR RS1
	CLR RS0
	
	PUSH Acc
	SWAP A
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT  ;显示分钟高位
	
	MOV A, #0CBH
	LCALL W_CMD
	
	POP Acc
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示分钟低位
	NOP

	LJMP BB 	 
;***************************************************

;***************闹钟路数2显示函数********************
 CLOCK_LINE_2_SHOW:

	MOV A, #0C4H	  ;写入位置
	LCALL W_CMD

	MOV A, #2
 	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT
;显示小时
	MOV A, #0C7H
	LCALL W_CMD

	SETB RS1
	SETB RS0
	MOV A, R2
	CLR RS1
	CLR RS0

	PUSH Acc
	SWAP A
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT  ;显示小时高位
	
	MOV A, #0C8H
	LCALL W_CMD
	
	POP Acc
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示小时低位

;显示符号-
	MOV A, #0C9H
	LCALL W_CMD
	MOV A, #3AH
	LCALL W_DAT

;显示分钟
	MOV A, #0CAH
	LCALL W_CMD

	SETB RS1
	SETB RS0
	MOV A, R3
	CLR RS1
	CLR RS0

	PUSH Acc
	SWAP A
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT  ;显示小时高位
	
	MOV A, #0CBH
	LCALL W_CMD
	
	POP Acc
	ANL A, #0FH
	ORL A, #30H
	LCALL W_DAT ;显示小时低位

	LJMP CC
;***************************************************

;************** 外部中断初始化函数 ******************
;外部中断0做小时设定
;外部中断1做分钟设定

EXTI_INTER_INIT:
	SETB IT0 ;外部中断0下降沿触发
	SETB EX0 ;打开外部中断0开关

	SETB IT1 ;外部中断1下降沿触发
	SETB EX1 ;打开外部中断1开关

	SETB EA  ;打开中断总开关

	RET
;***************************************************

;************** 外部中断0服务函数 ******************
;外部中断0做分钟设定

INT0_INTERRUPT:
	JB CLOCK_LINE_STATUS,CLOCK_SELECT_LINE_1_MIN
	AJMP CLOCK_SELECT_LINE_2_MIN
CLOCK_SELECT_LINE_1_MIN:

	SETB RS1
	SETB RS0
	MOV A, R1
	CJNE A,#59H,UNEQUAL_1_MIN
	MOV R1,#00H
	AJMP STOP_MIN
UNEQUAL_1_MIN:
	ANL A, #0FH
	CJNE A, #09H, PLUS_ONE_1_MIN
	MOV A, R1
	ANL A, #0F0H
	ADD A, #10H
	MOV R1,A
	AJMP STOP_MIN
PLUS_ONE_1_MIN:
    INC R1
	AJMP STOP_MIN

CLOCK_SELECT_LINE_2_MIN:
	SETB RS1
	SETB RS0
	MOV A, R3
	CJNE A,#59H,UNEQUAL_2_MIN
	MOV R3,#00H
	AJMP STOP_MIN
UNEQUAL_2_MIN:
	ANL A, #0FH
	CJNE A, #09H, PLUS_ONE_2_MIN
	MOV A, R3
	ANL A, #0F0H
	ADD A, #10H
	MOV R3,A
	AJMP STOP_MIN
PLUS_ONE_2_MIN:
    INC R3

STOP_MIN:
	CLR RS1
	CLR RS0	

	RETI
;***************************************************

;************** 外部中断1服务函数 ******************
;外部中断1做小时设定

INT1_INTERRUPT:
	JB CLOCK_LINE_STATUS,CLOCK_SELECT_LINE_1
	AJMP CLOCK_SELECT_LINE_2
CLOCK_SELECT_LINE_1:

	SETB RS1
	SETB RS0
	MOV A, R0
	CJNE A,#23H,UNEQUAL_1
	MOV R0,#00H
	AJMP STOP
UNEQUAL_1:
	ANL A, #0FH
	CJNE A, #09H, PLUS_ONE_1
	MOV A, R0
	ANL A, #0F0H
	ADD A, #10H
	MOV R0,A
	AJMP STOP
PLUS_ONE_1:
    INC R0
	AJMP STOP

CLOCK_SELECT_LINE_2:
	SETB RS1
	SETB RS0
	MOV A, R2
	CJNE A,#23H,UNEQUAL_2
	MOV R2,#00H
	AJMP STOP
UNEQUAL_2:
	ANL A, #0FH
	CJNE A, #09H, PLUS_ONE_2
	MOV A, R2
	ANL A, #0F0H
	ADD A, #10H
	MOV R2,A
	AJMP STOP
PLUS_ONE_2:
    INC R2

STOP:
	CLR RS1
	CLR RS0	
	RETI

;***************************************************

;****************** 闹钟1蜂鸣器判断 *********************

BUZ_1:
	SETB RS1
	SETB RS0
	
	NOP

	MOV A, R0
	CJNE A, HOUR , UNEQUAL_LINE_BUZ_1
	MOV A, R1
	CJNE A, MINUTE, UNEQUAL_LINE_BUZ_1
	SETB BUZZER
	MOV A, #00H
	MOV B, SECOND
	CJNE A, B, UNEQUAL_LINE_BUZ_1

//	MOV DPTR, #5000   ;调用延时函数
//	MOV A, #1
//	LCALL T0_DELAY
	
	CLR BUZZER 
UNEQUAL_LINE_BUZ_1:
	CLR RS0
	CLR RS1
	RET
;***************************************************

;****************** 闹钟2蜂鸣器判断 *********************

BUZ_2:
	SETB RS1
	SETB RS0
	

	NOP

	MOV A, R2
	MOV B, HOUR
	CJNE A, B , UNEQUAL_LINE_BUZ_2

	MOV A, R3
	MOV B, MINUTE
	CJNE A, B, UNEQUAL_LINE_BUZ_2

	MOV A, #00H
	MOV B, SECOND
	CJNE A, B, UNEQUAL_LINE_BUZ_2

	SETB BUZZER

 
UNEQUAL_LINE_BUZ_2:
	MOV A, #05H
	MOV B, SECOND 
    CJNE A, B, ABC


    CLR BUZZER

ABC:CLR RS0
	CLR RS1
	RET
;***************************************************



TAB1: DB 'BELL  TIME'

//CLOCK_HOUR_1: DB 11H
//CLOCK_HOUR_2: DB 12H
//CLOCK_MINUTE_1:	DB 11H
//CLOCK_MINUTE_2:	DB 12H


	END

运行结果:

在这里插入图片描述

说明:

DS1302芯片讲解地址:DS1302关于51汇编的驱动函数

程序下载地址:51汇编DS1302+LCD时间显示

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值