单片机之实现多功能手表

结构原理图

数码管

在这里插入图片描述

译码器

在这里插入图片描述

矩阵按键

在这里插入图片描述

51单片机

在这里插入图片描述

说明

  • 代码是利用定时器来计时,不建议这么做。
  • 普某的单片机有DS1302时钟芯片,利用其可以简单显示时间和日期。
  • 由于本人代码水平有限,以下代码写的比较难看,不好懂,请谅解。
  • 结构原理图源自普某的单片机的结构原理图
  • 实现功能有时间显示,日期显示,跑表,倒计时

汇编代码

timer0_count equ 40h
timer1_count equ 41h
DOWN0 equ 58h	;存放倒计时数据
DOWN1 equ 59h
DOWN2 equ 5ah
DOWN3 equ 5bh
DOWN4 equ 5ch
DOWN5 equ 5dh
DOWN6 equ 5eh
DOWN7 equ 5fh
STOP0 equ 60h
STOP1 equ 61h
STOP2 equ 62h
STOP3 equ 63h
STOP4 equ 64h
STOP5 equ 65h
STOP6 equ 66h
STOP7 equ 67h
DATA0 equ 68h	;存放日期 日 - 月 - 年
DATA1 equ 69h
DATA2 equ 6ah
DATA3 equ 6bh
DATA4 equ 6ch
DATA5 equ 6dh
DATA6 equ 6eh
DATA7 equ 6fh	
TIME0 equ 70h	;存放时间 秒 - 分 - 时 
TIME1 equ 71h
TIME2 equ 72h
TIME3 equ 73h
TIME4 equ 74h
TIME5 equ 75h
TIME6 equ 76h
TIME7 equ 77h
LED0 equ 78h	;LED显示数据首地址
LED1 equ 79h	
LED2 equ 7ah	
LED3 equ 7bh	
LED4 equ 7ch	
LED5 equ 7dh	
LED6 equ 7eh	
LED7 equ 7fh
keyvalue equ 50h
STOPCOUNT equ 51h	;记录数据计数的个数
STOPCOUNT_DIS equ 52h
COUNTSAVE xdata 00h	;计数器数据保存的首地址
BUZZ BIT P1.5

ORG 0000h
LJMP main
ORG 000bh		
LJMP timer0_ser		;定时器0中断入口地址
ORG 001bh		
LJMP timer1_ser		;定时器1中断入口地址
ORG 0003h
LJMP data_init		;外部中断0入口地址
ORG 0013h	
LJMP time_init		;外部中断1入口地址
ORG 0030h

main:
	MOV R0, #78h	;数码管首地址
	MOV R7, #08h	;八个数码管,数据分别放入78H, 79H, 7aH, 7bH, 7cH, 7dH, 7eH, 7fH
	MOV A, #00h	
	
	;位地址,逻辑控制,都初始化为0
	CLR C
	MOV 30h, C	;选择显示时间还是日期, C = 0进入时间, C = 1进入日期
	
	MOV 31h, C	;用来控制LED2,LED4,LED6显示字形码,(31) = 0选择字形码tab_sign (默认),(31h) = 1选择tab_carray_dp
	
	MOV 32h, C  ;选择数码管显示并保存还是将保存时间、日期并不显示 (32) = 0显示并保存 (32) = 1不显示但保存
	
	MOV 33h, C	;在跑表按键(33h) = 0计数,(33h) = 1跑表清零, 在按键编号0a上使用
	
	MOV 34h, C	;在按键编号0c上使用 (34) = 0进入倒计时设置功能,(34) = 1启动倒计时
	
	MOV 35h, C	;在按键编号0c上使用 (35) = 0无功能, (35) = 1要显示倒计时数码
	
	MOV 36h, C	;(36) = 0倒计时每秒响一下,(36) = 1倒计时不响
	
	MOV STOPCOUNT, #01h;跑表计数初始化为1
	MOV STOPCOUNT_DIS, #01h
	
	;新建240个字节大小,记录60个跑表数据,每个数据占4个字节
	MOV DPTR, #04h	;从04开始保存
	MOV R1, #24
	
	new_init:
	MOV R2, #10
	
	new_next:
	MOV A, #00h
	MOVX @DPTR, A
	INC DPTR
	new1:
	DJNZ R2, new_next
	DJNZ R1, new_init
	
;一些基本设置
lp:
	MOV @R0, A	;进行初始化,全为0
	INC R0
	DJNZ R7, lp

	MOV tmod, #11h	;设置定时功能,定时器0模式1, 定时器1模式1
	MOV th0, #0D8h	;定时器初始化,定时1s
	MOV tl0, #0F0h
	MOV th1, #0D8h
	MOV tl1, #0F0h
	MOV timer0_count, #064h	;定时次数100,1s
	MOV timer1_count, #064h	;定时次数100,1s

	SETB EA		;开启总中断
	SETB ET0	;开启定时0中断
	SETB ET1	;开启定时1中断
	
	SETB EX0	;打开外部中断0
	CLR IT0		;设置外部中断0触发方式
	SETB EX1	;打开外部中断1
	CLR IT1		;设置外部中断1触发方式

main1:
	LCALL disp
	LCALL keycan
	SJMP main1

disp:
	MOV R0,#78h	;数码管数据首地址
	MOV R7,#00h	;
	SJMP dis_loop
	RET

;动态数码管显示
dis_loop:
	MOV DPTR, #sub
	MOV A, R7	;0000 0000B
	RL A	;0000 0000B
	JMP @A+DPTR	;跳转到sub
	;八个数码管,每个显示1ms
sub:
	AJMP s1
	AJMP s2
	AJMP s3
	AJMP s4
	AJMP s5
	AJMP s6
	AJMP s7
	AJMP s8
s1:
	CLR p2.2
	CLR p2.3
	CLR p2.4
	SJMP s_normal	;000
s2:
	SETB p2.2
	CLR p2.3
	CLR p2.4	;001
	SJMP s_normal
s3:
	CLR p2.2
	SETB p2.3
	CLR p2.4	;010
	MOV C, 31h
	JC s_dp	;(31h) = 1跳转到s_dp 否则 (31h) = 0跳转到s_sign
	;不可写成 JB 31h, s_dp	程序会出现问题
	SJMP s_sign
s4:
	SETB p2.2
	SETB p2.3
	CLR p2.4	;011
	SJMP s_normal
s5:
	CLR p2.2
	CLR p2.3
	SETB p2.4	;100
	MOV C, 31h
	JC s_dp	;(31h) = 1跳转到s_dp 否则 (31h) = 0跳转到s_sign
	SJMP s_normal
s6:
	SETB p2.2
	CLR p2.3
	SETB p2.4	;101
	MOV C, 31h
	JC s_normal		;(31h) = 1跳转到s_normal 否则 (31h) = 0跳转到s_sign,LED5正常显示,不需要加点
	SJMP s_sign
s7:
	CLR p2.2
	SETB p2.3
	SETB p2.4	;110
	MOV C, 31h
	JC s_dp	;(31h) = 1跳转到s_dp 否则 (31h) = 0跳转到s_sign
	SJMP s_normal
s8:
	SETB p2.2
	SETB p2.3
	SETB p2.4	;111 cba
	SJMP s_normal
;显示正常字形码
s_normal:
	MOV DPTR, #tab ;字型表头地址→DPTR
	MOV A, @R0	;取显示数据
	
	common:
	MOVC A, @A+DPTR	;取出字形码
	MOV p0, A	;送出显示
	
	LCALL delay2ms ;延时,视觉暂留
	INC R7	
	INC R0
	MOV A, R7
	CJNE A, #08, dis_loop ;8个数码管,是否扫到最后一个数码管
	RET	;子程序返回
;显示扩折号 -
s_sign:
	MOV DPTR, #tab_sign
	MOV A, #00h
	SJMP common ;理由同下
	RET
;显示带点的数值 LED2,LED4,LED6
s_dp:
	MOV DPTR, #tab_carray_dp ;字型表头地址→DPTR
	MOV A, @R0	;取显示数据
	SJMP common	;由于报 TARGET OUT OF RANGE错误,就写成这样
	RET
;延时2ms,人眼视觉暂留
delay2ms:
	MOV R3,#0ah	

	d2:	
	MOV R4,#064h

	d1:	
	DJNZ R4,d1	
	DJNZ R3,d2
	RET
;/延时

time_rtn:
		RETI

;定时器0中断入口地址
timer0_ser:
	MOV th0, #0D8h ; 0011 1100B
	MOV tl0, #0F0h	;1011 0000B
	;设置定时器每10ms秒溢出一次
	;进入这里意为显示时间而非跑表
	;溢出次数少于100次,中断返回就是说把中断标志清零,重新中断
	DJNZ timer0_count, time_rtn	;timer0_count初值为100,
	MOV  timer0_count, #64h ;计数100次
	
	MOV C, 32h
	JC save_time_trans
	
	MOV C, 35h	;判断是否倒计时
	JC down_time_trans	;为1则跳转到倒计时,为0顺序执行
	
	MOV C, 30h	;判断显示时间还是日期
	JNC time_dis
	SJMP data_dis
	RETI
	
	down_time_trans:	;太长,需要中转
	LJMP down_time
	RETI
	
;日期
;时间不停,继续走
data_dis:
	;显示
	MOV LED0, DATA0
	MOV LED1, DATA1
	MOV LED2, DATA2
	MOV LED3, DATA3
	MOV LED4, DATA4
	MOV LED5, DATA5
	MOV LED6, DATA6
	MOV LED7, DATA7
	
	save_time_trans:
	ACALL save_time
	RETI
	
;时间
time_dis:
	;显示
	MOV LED0, TIME0
	MOV LED1, TIME1
	MOV LED2, TIME2
	MOV LED3, TIME3
	MOV LED4, TIME4
	MOV LED5, TIME5
	MOV LED6, TIME6
	MOV LED7, TIME7
	CPL  BUZZ        ;对蜂鸣器进行取反
	time_dis_next:
	INC LED0		;计时1s后,秒位加1
	INC TIME0		;保存数据
	MOV A, LED0
	CJNE A, #0ah, time_rtn0
	;判断秒位是否等于10,等于10就进位,并且顺序执行
	;不等于0,也就是说(a)<10,中断返回,重新计时1s
	;等于10,需要进位
	MOV LED0, #0	;将秒个位清零
	MOV TIME0, #0
	INC LED1	;将秒十位加一/进一
	;
	INC TIME1
	MOV A, LED1
	CJNE A, #06h, time_rtn0 	;将秒十位逢六进一
	MOV LED1, #0	;将秒十位清零
	MOV TIME1, #0	;保存数据
	INC LED3
	;
	INC TIME3
	MOV A, LED3
	CJNE A, #0ah, time_rtn0
	MOV LED3, #0
	MOV TIME3, #0	;保存数据
	INC LED4	;分个位逢十进一
	;
	INC TIME4
	MOV A, LED4
	CJNE A, #06h, time_rtn0
	MOV LED4, #0
	MOV TIME4, #0	;保存数据
	INC LED6 ;分十位逢六进一,进位带时个位
	;
	;由于LED6进位可能过了24,需要清零
	MOV A, LED7
	CJNE A, #02h, time_rtn0
	MOV A, LED6
	CJNE A, #04h, time_rtn0
	MOV LED6, #0
	MOV LED7, #0
	MOV TIME6, #0	;保存数据
	MOV TIME7, #0	;保存数据
	INC DATA0	;24小时到了,加一天
	
	INC TIME6	;保存数据
	MOV A, LED6
	CJNE A, #0ah, time_rtn0
	MOV LED6, #0
	MOV TIME6, #0	;保存数据
	INC LED7	;时个位逢十进一
	INC TIME7
	RETI
	
	time_rtn0:
	RETI
;倒计时显示	
down_time:
	;显示
	MOV LED0, DOWN0
	MOV LED1, DOWN1
	MOV LED3, DOWN3
	MOV LED4, DOWN4
	MOV LED6, DOWN6
	MOV LED7, DOWN7
	MOV C, 36h
	JC time_0
	CPL  BUZZ        ;对蜂鸣器进行取反
	LCALL save_time
	
	time_0:
	MOV A, DOWN0
	CJNE A, #00h, down_time_0
	MOV LED0, #9
	MOV DOWN0, #9
	SJMP time_1
	down_time_0:
	DEC LED0
	DEC DOWN0
	SJMP down_rtn
	
	time_1:
	MOV A, DOWN1
	CJNE A, #00h, down_time_1
	MOV LED1, #5
	MOV DOWN1, #5
	SJMP time_2
	down_time_1:
	DEC LED1
	DEC DOWN1
	SJMP down_rtn
	
	time_2:
	MOV A, DOWN3
	CJNE A, #00h, down_time_2
	MOV LED3, #9
	MOV DOWN3, #9
	SJMP time_3
	down_time_2:
	DEC LED3
	DEC DOWN3
	SJMP down_rtn
	
	time_3:
	MOV A, DOWN4
	CJNE A, #00h, down_time_3
	MOV LED4, #5
	MOV DOWN4, #5
	SJMP time_4
	down_time_3:
	DEC LED4
	DEC DOWN4
	SJMP down_rtn
	
	time_4:
	MOV A, DOWN6
	CJNE A, #00h, down_time_4
	MOV LED6, #9
	MOV DOWN6, #9
	SJMP time_5
	down_time_4:
	DEC LED6
	DEC DOWN6
	SJMP down_rtn
	
	time_5:
	MOV A, DOWN7
	CJNE A, #00h, down_time_5
	SJMP time_end
	down_time_5:
	DEC LED7
	DEC DOWN7
	SJMP down_rtn
	
	down_rtn:
		RETI

	time_end:
	MOV A, LED6
	CJNE A, #09h, down_rtn
	MOV A, LED4
	CJNE A, #05h, down_rtn
	MOV A, LED3
	CJNE A, #09h, down_rtn
	MOV A, LED1
	CJNE A, #05h, down_rtn
	MOV A, LED0
	CJNE A, #09h, down_rtn
	MOV DOWN0, #0
	MOV DOWN1, #0
	MOV DOWN3, #0
	MOV DOWN4, #0
	MOV DOWN6, #0
	MOV DOWN7, #0
	MOV LED0, #0
	MOV LED1, #0
	MOV LED3, #0
	MOV LED4, #0
	MOV LED6, #0
	MOV LED7, #0
	SETB C
	MOV 36h, C	;让蜂鸣器停止响
	SJMP down_rtn
	RETI
;/定时器0中断结束范围

;保存时间程序入口
;在显示日期、显示秒表等功能时,需要将现在的时间保存并进行判断进位
save_time:
	INC TIME0		;计时1s后,秒位加1
	MOV A, TIME0
	CJNE A, #0ah, save_rtn	
	;判断秒位是否等于10,等于10就进位,并且顺序执行
	;不等于0,也就是说(a)<10,中断返回,重新计时1s
	;等于10,需要进位
	MOV TIME0, #0	;将秒个位清零
	INC TIME1	;将秒十位加一/进一
	;
	MOV A, TIME1
	CJNE A, #06h, save_rtn 	;将秒十位逢六进一
	MOV TIME1, #0	;将秒十位清零 ;保存数据
	INC TIME3
	;
	MOV A, TIME3
	CJNE A, #0ah, save_rtn
	MOV TIME3, #0	;保存数据
	INC TIME4	;分个位逢十进一
	;
	MOV A, TIME4
	CJNE A, #06h, save_rtn
	MOV TIME4, #0	;保存数据
	INC TIME6 ;分十位逢六进一,进位带时个位
	;
	;由于LED6进位可能过了24,需要清零
	MOV A, TIME7
	CJNE A, #02h, save_rtn
	MOV A, TIME6
	CJNE A, #04h, save_rtn
	MOV TIME6, #0	;保存数据
	MOV TIME7, #0	;保存数据
	INC DATA0	;24小时到了,加一天

	MOV A, TIME6
	CJNE A, #0ah, save_rtn
	MOV TIME6, #0	;保存数据
	INC TIME7	;时个位逢十进一
	RET
	save_rtn:
	RET

;定时器1中断入口地址
;秒表显示程序	
timer1_ser:
	MOV th1, #0D8h
	MOV tl1, #0F0h
	;显示
	MOV LED0, STOP0
	MOV LED1, STOP1
	MOV LED2, STOP2
	MOV LED3, STOP3
	MOV LED4, STOP4
	MOV LED5, STOP5
	MOV LED6, STOP6
	MOV LED7, STOP7
	
	DJNZ timer1_count, stop_next;timer0_count初值为100,
	MOV  timer1_count, #64h ;计数100次
	ACALL save_time
	RETI
	
	stop_next:
	;定时器0每10ms秒溢出一次,百分之一秒
	;百分之一秒
	INC LED0	;每10ms进行一次加一
	INC STOP0	;跑表内容保存
	MOV A, LED0
	CJNE A, #0ah, time_rtn1
	MOV LED0, #0	;进位需清零
	MOV STOP0, #0	;备份数据也要进行清理
	INC LED1	;进位加一
	INC STOP1
	
	MOV A, LED1
	CJNE A, #0ah, time_rtn1
	MOV LED1, #0
	MOV STOP1, #0
	INC LED2
	INC STOP2
	;秒
	MOV A, LED2
	CJNE A, #0ah, time_rtn1
	MOV LED2, #0
	MOV STOP2, #0
	INC LED3
	INC STOP3
	
	MOV A, LED3
	CJNE A, #06h, time_rtn1
	MOV LED3, #0
	MOV STOP3, #0
	INC LED4
	INC STOP4
	;分
	MOV A, LED4
	CJNE A, #0ah, time_rtn1
	MOV LED4, #0
	MOV STOP4, #0
	INC LED5
	INC STOP5
	
	MOV A, LED5
	CJNE A, #06h, time_rtn1
	MOV LED5, #0
	MOV STOP5, #0
	INC LED6
	INC STOP6
	;小时的个位有两种情况一种是位4,小时有可能是24,需要进位清零,还有可能是0-9
	MOV A, LED7
	CJNE A, #02h, time_rtn1
	MOV A, LED6
	CJNE A, #04h, time_rtn1
	MOV LED6, #0
	MOV LED7, #0
	MOV STOP6, #0
	MOV STOP7, #0
	;时
	MOV A, LED6
	CJNE A, #0ah, time_rtn1
	MOV LED6, #0
	MOV STOP6, #0
	INC LED7	;时个位逢十进一
	INC STOP6
	RETI
	time_rtn1:
		RETI
;/定时器1中断出口

;外部中断0入口地址,初始化日期
data_init:
	MOV DATA0, #01h
	MOV DATA1, #00h
	MOV DATA2, #00h
	MOV DATA3, #06h
	MOV DATA4, #00h
	MOV DATA5, #00h
	MOV DATA6, #09h
	MOV DATA7, #01h
	
	SETB C ;选择显示日期
	MOV 30h, C
	SETB TR0
	RETI
	
;外部中断1入口地址,初始化时间
time_init:
	MOV TIME0, #00h
	MOV TIME1, #00h
	MOV TIME2, #00h
	MOV TIME3, #00h
	MOV TIME4, #00h
	MOV TIME5, #00h
	MOV TIME6, #02h
	MOV TIME7, #01h
	
	CLR C ;选择显示时间
	MOV 30h, C
	SETB TR0
	RETI

;延时10ms,消除机械抖动
delay10ms:
	MOV r3,#10h	

	TS1:	
	MOV r4,#0ffh

	TS2:	
	DJNZ r4,TS2	
	DJNZ r3,TS1
	RET
;/延时

;键盘扫描
; p1.7 p1.6 p1.5 p1.4行端口
; p1.3 p1.2 p1.1 p1.0列端口
keycan:
	MOV R3, #00h	;row	行
	MOV R4, #00h	;col	列
						
	MOV p1, #0fh	;向行输入0,列输入1 
	NOP 	;使p1端口输出稳定
	NOP
	MOV A, p1	;读取列值
						
	CJNE A, #0fh, k1	
	;判断a中列是否有变为0的
	;有低四位0值表示存在按键,跳转到k1中处理
	;低四位全1值表示没有按键,A的内容就不会等于0fh,就会跳转到keyrtn返回子程序 36行
	SJMP keyrtn
	
	;另外写法
	;CPL A ;对A的内容取反
	;ANL A, #0fh	;消去行的值
	;JZ keyrtn	;A的内容全为零表示没有键按下
	
	;确定行和列是通过分别给列和行送零并读取行和列数值
	;进行一些列操作后,再读取p1端口值判断那一列和那一行

k1:
	LCALL delay10ms ;延时10ms消除机械抖动
	MOV A, p1	;重新读取列值
	
	CPL A
	ANL A, #0fh
	JB acc.0, row1	;确定哪一列被按下键
	JB acc.1, row2
	JB acc.2, row3
	JB acc.3, row4
row1:
	MOV R4, #00h	;第0列
	SJMP next
row2:
	MOV R4, #01h	;第1列
	SJMP next
row3:
	MOV R4, #02h	;第2列
	SJMP next
row4:
	MOV R4, #03h	;第3列
	SJMP next

;接下来判断按键是哪一行的
next:
	MOV p1, #0f0h	;行送1,列送0
	NOP 
	NOP
	MOV A, p1	;读行值
						
	CJNE A, #0fh, k2	;同上
	SJMP keyrtn
k2:
	CPL A
	ANL A, #0f0h
	;SWAP a	;交换半个字节
	;JB acc.0, col1
	;JB acc.1, col2
	;JB acc.2, col3
	;JB acc.3, col4	
	JB acc.4, col1
	JB acc.5, col2
	JB acc.6, col3
	JB acc.7, col4

col1:
	MOV R3, #00h	;第一行
	SJMP keyhandle
col2:
	MOV R3,#01h		;第二行
	SJMP keyhandle	
col3:
	MOV R3,#02h		;第三行
	SJMP keyhandle
col4:
	MOV R3,#03h		;第四行
	SJMP keyhandle

;键值译码
keyhandle:
	MOV A, R3	;r3行 r4列
	MOV B, #04h
	MUL AB
	ADD A, R4
	MOV keyvalue, A	;键值放入keyvalue中
	
	;再一次判断是否有键按下
	keywait:	
	MOV p1, #0fh
	MOV A, p1
	CJNE A, #0fh, keywait
	LCALL handlekey
	
keyrtn:
	RET

handlekey:
	MOV A, keyvalue
	CJNE A, #00h, set_second		;如果不是第一个按键跳set_second
	
	CLR C
	MOV 30h, C	;按一下 进入时间设置
	
	MOV 31h, C	;LED2和LED5选择字形码tab_sign
	
	MOV 32h, C	;重新启动时间显示(从跑表功能)
	
	MOV 35h, C	;可以通过此按键从倒计时功能回到时间显示功能
	
	MOV 36h, C	;设置蜂鸣器可以响
	
	SETB TR0	;开启定时器0
	CLR TR1		;关闭定时器1
	;通过定时器选择进行选择秒表功能和显示时间功能的选择
	RET ;返回到handlekey

;设置时钟秒
set_second:
	MOV A, keyvalue	
	CJNE A, #01h, set_minute
	INC LED0
	INC TIME0
	MOV A, LED0
	
	CJNE A, #0ah, timertn	;判断秒的个位是否需要进位,大于10进位
	MOV LED0, #0	;需要进位,将秒个位清零,秒十位加一
	MOV TIME0, #0	;将数据保存在TIME中
	INC LED1
	INC TIME1
	
	MOV A, LED1
	CJNE A, #06h, timertn	;判断秒的十位是否需要进位,大于6进位
	MOV LED1, #0			;需要进位,将秒十位清零,分个位加一
	MOV TIME1, #0
	INC LED3
	INC TIME3	
	RET
	
set_minute:
	MOV A, keyvalue	
	CJNE A, #02h, set_hour
	
	INC LED3
	INC TIME3
	MOV A, LED3
	CJNE A, #0ah, timertn
	MOV TIME3, #0
	MOV LED3, #0
	
	INC LED4
	INC TIME4
	MOV A, LED4
	CJNE A, #06h, timertn
	MOV LED4, #0
	MOV TIME4, #0
	INC LED6 ;分十位逢六进一
	INC TIME6
	
	;小时进一位有可能到达24,需求置零
	MOV A, LED7
	CJNE A, #02h, timertn
	MOV A, LED6
	CJNE A, #04h, timertn
	MOV LED6, #0
	MOV TIME6, #0
	MOV LED7, #0
	MOV TIME7, #0
	RET
	
timertn:
	RET	

set_hour:
	MOV A, keyvalue	
	CJNE A, #03h, datakey_init
	
	;对小时操作也需要考虑是否超24,清零
	MOV A, LED7
	CJNE A, #02h, hour

	INC LED6
	INC TIME6
	MOV A, LED6
	CJNE A, #04h, timertn
	MOV LED6, #0
	MOV LED7, #0
	MOV TIME6, #0
	MOV TIME7, #0
	RET
	
	hour:
	INC LED6
	INC TIME6
	MOV A, LED6
	
	CJNE A, #0ah, timertn
	MOV LED6, #0
	MOV TIME6, #0
	INC TIME7
	INC LED7
	RET
	
datakey_init:
	MOV A, keyvalue	;第二行第一个按键	
	;一年 12个月 每个月30天 
	CJNE A, #04h, day_control	
	SETB C
	MOV 30h, C	;重新启动日期显示(从时间功能)
	CLR C
	MOV 31h, C	;LED2和LED5选择字形码tab_sign
	
	MOV 32h, C	;重新启动日期显示(从跑表功能)
	
	MOV 35h, C	;可以通过此按键从倒计时功能回到日期显示功能
	
	MOV 36h, C	;设置蜂鸣器可以响
	
	SETB TR0	;开启定时器0
	CLR TR1		;关闭定时器1
	RET

day_control:			;第二行第二个按键
	MOV A, keyvalue
	CJNE A, #05h, month_control	
	INC LED0
	INC DATA0
	MOV A, LED0
	
	CJNE A, #0ah, datartn	;天个位判断是否需要进位
	MOV LED0, #0
	MOV DATA0, #0
	INC LED1
	INC DATA1
	
	MOV A, LED1
	CJNE A, #03h, datartn	;天十位判断是否需要进位
	MOV LED1, #0		
	MOV DATA1, #0
	INC LED3
	INC DATA3
	RET

datartn:	;放在这里是避免超过256B
	RET	

month_control:			;第二行第三个按键
	MOV A, keyvalue
	CJNE A, #06h, year_control	;不等于跳转到year_control中
	
	INC LED3
	INC DATA3
	
	;月的个位进位了,需要判断
	MOV A, LED3
	CJNE A, #03, month
	MOV A, LED4
	CJNE A, #01, month
	MOV LED3, #1
	MOV LED4, #0
	INC LED6
	MOV DATA3, LED3
	MOV DATA4, LED4
	MOV DATA6, LED6
	
	;年的个位进位,需要判断是否进位
	MOV A, LED6
	SJMP year

	month:
	;不需要向年进位
	MOV A, LED3
	CJNE A, #0ah, datartn	;月个位判断是否需要进位
	MOV LED3, #0
	MOV DATA3, #0
	INC LED4
	INC DATA4
	RET
	
year_control:
	MOV A, keyvalue
	CJNE A, #07h, stopwatchkey_init
	
	INC LED6
	MOV DATA6, LED6
	MOV A, LED6
	
	year:	
	CJNE A, #0ah, datartn	;月个位判断是否需要进位
	MOV LED6, #0
	MOV DATA6, #0
	INC LED7
	INC DATA7
	RET
	
stopwatchkey_init:
	;此按键功能是选择跑表功能,并开始跑表
	MOV A, keyvalue
	CJNE A, #08h, stopwatch_stop
	CLR TR0		;关闭定时器0
	SETB TR1		;关闭定时器1
	
	SETB C
	MOV 31h, C ;选择字形码tab_carray_dp
	
	CLR C
	MOV 35h, C	;可以通过此按键从倒计时功能回到日期显示功能
	MOV 36h, C	;设置蜂鸣器可以响
	
	MOV STOPCOUNT, 01h ;初始化计数的次数
	RET
stopwatch_stop:
	;此按键功能是暂停跑表和进行跑表
	MOV A, keyvalue
	CJNE A, #09h, stopwatch_count_clear
	CPL TR0	;定时器0的开启或关闭
	CPL TR1	;定时器1的开启或关闭
	;选择选择字形码tab_carray_dp
	SETB  C
	MOV 31h, C
	
	MOV C, 32h	;32h中内容为1,这时间继续跑,但不显示在数码管上
	CPL C	;取反
	MOV 32h, C
	
	SETB C
	MOV 33h, C;设置按键0a为清零功能
	
	;将此时暂停数显示数码管上
	MOV R0, #LED0	;首地址
	MOV R1, #STOP0 ;首地址
	MOV R5, #08		;循环8次
	
	stop_loop:
	MOV A, @R1
	MOV @R0, A
	INC R0
	INC R1
	DJNZ R5, stop_loop
	RET
	
stopwatch_count_clear:
	;此按键功能是对清零跑表和跑表计数的
	MOV A, keyvalue
	CJNE A, #0ah, stopwatch_display
	
	;利用33h中内容进行按键功能选择
	MOV C, 33h
	JNC stopwatch_count	;为0计数,为1清零
	stopwatch_clear:	;跑表清零
	;对STOP0~STOP1赋值0 , 对显示LED0~LED1赋值0
	MOV R5, #08
	MOV R1, #STOP0
	MOV R0, #LED0
	;CLR C
	;MOV C, 33h
	
	clear_loop:		
	MOV @R1, #0
	MOV @R0, #0
	INC R1
	INC R0
	DJNZ R5, clear_loop
	
	CLR C
	MOV 33h, C;清零后,将按键功能设置为默认计数功能
	
	MOV STOPCOUNT, #01h;跑表计数器清零
	RET
	
	;计数功能
	stopwatch_count:
	;跳到记录的位置
	MOV DPTR, #COUNTSAVE	;并非数据存放的首地址,首地址是04h
	MOV R0, STOPCOUNT
	jmp_address:
	INC DPTR	;后移四个字节
	INC DPTR
	INC DPTR
	INC DPTR
	DJNZ R0, jmp_address
	;此时DPTR指向需要记录的地址空间首地址
	INC STOPCOUNT	;每按一下,计数一次

	;八个数码管的数据,四个字节空间存储
	CLR C
	MOV R0, LED0	;百分之秒的个位
	MOV A, LED1	;百分之秒的十位
	MOV B, #10
	MUL AB	;值小等于90
	ADD A, R0
	MOVX @DPTR, A
	INC DPTR
	
	MOV R0, LED2	;秒的个位
	MOV A, LED3	;秒的十位
	MOV B, #10
	MUL AB	;值小等于90
	ADD A, R0
	MOVX @DPTR, A
	INC DPTR
	
	MOV R0, LED4	;分的个位
	MOV A, LED5	;分的十位
	MOV B, #10
	MUL AB	;值小等于90
	ADD A, R0
	MOVX @DPTR, A
	INC DPTR
	
	MOV R0, LED6	;时的个位
	MOV A, LED7	;时的十位
	MOV B, #10
	MUL AB	;值小等于90
	ADD A, R0
	MOVX @DPTR, A	;存在片外RAM中
	INC DPTR
	
	RET
	stoprtn:
	RET
	
stopwatch_display:
	;此按键功能是对跑表计数显示
	MOV A, keyvalue
	CJNE A, #0bh, countdownkey_init
	
	CLR TR1
	SETB TR0
	
	SETB C
	MOV 32h, C	;不显示,定时器0继续计时,TIME0
	
	;跳到记录的位置
	MOV DPTR, #COUNTSAVE	;非数据存放的首地址
	MOV R0, STOPCOUNT_DIS
	jmp_address2:
	INC DPTR	;后移四个字节
	INC DPTR
	INC DPTR
	INC DPTR
	DJNZ R0, jmp_address2
	;此时DPTR指向需要记录的地址空间首地址
	INC STOPCOUNT_DIS	;每按一下,计数显示一次
	
	stopwatch_display_next:
	MOVX A, @DPTR
	MOV B, #10
	DIV AB	;余数在b中,商在a中
	MOV LED0, B
	MOV LED1, A
	INC DPTR
	
	MOVX A, @DPTR
	MOV B, #10
	DIV AB	;余数在b中,商在a中
	MOV LED2, B
	MOV LED3, A
	INC DPTR
	
	MOVX A, @DPTR
	MOV B, #10
	DIV AB	;余数在b中,商在a中
	MOV LED4, B
	MOV LED5, A
	INC DPTR
	
	MOVX A, @DPTR
	MOV B, #10
	DIV AB	;余数在b中,商在a中
	MOV LED6, B
	MOV LED7, A
	INC DPTR
	
	stop_rtn:
	RET
	
countdownkey_init:
	;此按键功能是开启倒计时功能或启动倒计时功能
	MOV A, keyvalue
	CJNE A, #0ch, countdown_second
	SETB TR0	;开启定时器0
	CLR TR1		;关闭定时器1
	
	;倒计时字形码在sub子程序中进行判断选择
	
	;(34) = 0进入倒计时设置功能,(34) = 1启动倒计时
	;通过按键编号为0d, 0e, 0f进行修改34h中的内容
	MOV C, 34h
	JC countdown
	countdown_set:
	SETB C
	MOV 32h, C	;不显示,定时器0继续计时,TIME0
	CLR C
	MOV 31h, C	;LED2, LED5显示sub_sign即显示-
	;清零,启动倒计时功能
	MOV R5, #08
	MOV R1, #DOWN0
	MOV R0, #LED0
	LJMP clear_loop
	RET
	
	countdown:
	;倒计时开始倒计时
	;显示内容
	MOV R5, #08
	MOV R1, #DOWN0
	MOV R0, #LED0
	down_dis_loop:		;
	MOV A, @R1
	MOV @R0, A
	INC R1
	INC R0
	DJNZ R5, down_dis_loop
	;在定时器0中断处理和显示倒计时
	SETB C
	MOV 35h, C
	CLR C
	MOV 32h, C	;显示,定时器0继续计时,ELD倒计时
	MOV 34h, C	;34h的内容设置为0,在按一次此按键将进入countdown_set中
	MOV 36h, C
	RET
countdown_second:
	;此按键功能是设置倒计时的秒位
	MOV A, keyvalue
	CJNE A, #0dh, countdown_minute
	
	;设置完毕,可以开始倒计时
	SETB C
	MOV 34h, C
	
	INC LED0
	INC DOWN0
	MOV A, LED0
	
	CJNE A, #0ah, countrtn	;判断秒的个位是否需要进位,大于10进位
	MOV LED0, #0	;需要进位,将秒个位清零,秒十位加一
	MOV DOWN0, #0	;将数据保存在DOWN中
	INC LED1
	INC DOWN1
	
	MOV A, LED1
	CJNE A, #06h, countrtn	;判断秒的十位是否需要进位,大于6进位
	MOV LED1, #0			;需要进位,将秒十位清零,分个位加一
	MOV DOWN1, #0
	INC LED3
	INC DOWN3
	
	RET
countdown_minute:
	;此按键功能是设置倒计时的分位
	MOV A, keyvalue
	CJNE A, #0eh, countdown_hour
	
	;设置完毕,可以开始倒计时
	SETB C
	MOV 34h, C
	
	INC LED3
	INC DOWN3
	MOV A, LED3
	CJNE A, #0ah, countrtn
	MOV DOWN3, #0
	MOV LED3, #0
	
	INC LED4
	INC DOWN4
	MOV A, LED4
	CJNE A, #06h, countrtn
	MOV LED4, #0
	MOV DOWN4, #0
	INC LED6 ;分十位逢六进一
	INC DOWN6
	
	;小时进一位有可能到达24,需求置零
	MOV A, LED7
	CJNE A, #02h, countrtn
	MOV A, LED6
	CJNE A, #04h, countrtn
	MOV LED6, #0
	MOV DOWN6, #0
	MOV LED7, #0
	MOV DOWN7, #0
	
	RET
countrtn:
	RET
countdown_hour:
	;此按键功能是设置倒计时的小时位
	MOV A, keyvalue
	CJNE A, #0fh, countrtn
	
	;设置完毕,可以开始倒计时
	SETB C
	MOV 34h, C
	
	;对小时操作也需要考虑是否超24,清零
	MOV A, LED7
	CJNE A, #02h, count_hour

	INC LED6
	INC DOWN6
	MOV A, LED6
	CJNE A, #04h, countrtn
	MOV LED6, #0
	MOV LED7, #0
	MOV DOWN6, #0
	MOV DOWN7, #0
	RET
	
	count_hour:
	INC LED6
	INC DOWN6
	MOV A, LED6
	
	CJNE A, #0ah, countrtn
	MOV LED6, #0
	MOV DOWN6, #0
	INC DOWN7
	INC LED7
	RET

tab: 
	DB  3Fh,06h,5bh,4fh,66h,6dh,7dh,07h,7fh,6fh,77h,7ch,00h	;共阴极7段LED显示字型编码 
		;0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, 熄灭
tab_carray_dp:
	DB	0bfh, 86h, 0dbh, 0cfh, 0e6h, 0edh, 0fdh, 87h, 0ffh, 0efh, 0f7h, 0fch, 80h
		;0. , 1. , 2. , 3. , 4. , 5. , 6. , 7. , 8. , 9. , a. , b. , 熄灭
tab_sign:
	DB 	40h ; -
END
; 0:0000 1:0001	2:0010	3:0011	4:0100	5:0101	6:0110	7:0111
; 8:1000 9:1001	A:1010	B:1011	C:1100	D:1101	E:1110	F:1111
发布了20 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/jianming21/article/details/92162011