结构原理图
数码管
译码器
矩阵按键
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