51单片机8位数码管时钟(汇编语言)
作业要求
使用8位数码管实现时间的显示,进位等操作
使用汇编语言完成作业
最终效果
理论基础
寻址
汇编语言的一些基本寻址操作
MOV A,#20H
MOV R1,20H
MOV @R1,#20H
MOV A,@R1
需要理解用法和含义
中断
实现计时器需要使用中断来完成延时
如果使用循环延迟的话,在计时过程中处理器不能进行其他操作
位码
设置具体的显示位,如 00H 是数码管的第一位
本例中使用 P2 引脚输出位码
段码
输出图形的控制
本例中使用 P1 引脚
如 40H 为 -
显示
一个数字的显示需要位码和段码同时起作用
如 P1 输入 #00H, P2 输入 #40H
在数码管的第一位会显示 “-”
这里显示需要不连续的进行显示
(数字和"-"需要分开)
所以只用一个地址位去缓存位码是不够的
(如果是连续显示,不断修改buffer为位码就行了)
这里采用了6个地址空间去对位码进行缓存
MOV index,#00H
MOV cDisplayBit,#00H
MOV cDisplayBit+1,#01H
MOV cDisplayBit+2,#03H
MOV cDisplayBit+3,#04H
MOV cDisplayBit+4,#06H
MOV cDisplayBit+5,#07H
其中 02H 和 05H需要显示为"-",我使用另外一个子程序去进行显示
显示缓冲区
本例中使用 cDisplayBuffer 表示 20H,并将 20-25H 这六位作为显示缓冲区
存储需要显示的数字的原值,如1,2,3
从指定地址中取值然后转换为段码再配合位码即可实现显示
cDisplayBuffer 作为 位码的缓存位
难点
中间需要显示"-",所以位码缓存位只使用1位是不够的,详细参见下列代码拆分
结构
内存设置
ORG 0000H
SJMP MAIN
ORG 000BH
LJMP SER0
cDisplayBuffer EQU 20H ;
cDisplayBit EQU 40H ;当前显示的位 40-45;47 46固定显示-
index EQU 50H ;作为遍历index使用
设置初值
需要写到MAIN函数中
MAIN:
MOV index,#00H
MOV cDisplayBit,#00H
MOV cDisplayBit+1,#01H
MOV cDisplayBit+2,#03H
MOV cDisplayBit+3,#04H
MOV cDisplayBit+4,#06H
MOV cDisplayBit+5,#07H
中断延迟程序
MOV SP,#70H ;设置堆栈SP
CLR EA ;关中断
MOV 30H,#23H ;设置初始时间值
MOV 31H,#59H
MOV 32H,#56H
MOV TMOD,#01H ;使用定时器0,软件控制,定时模式,方式1
MOV TH0,000H ;定时初值
MOV TL0,0D8H
MOV R6,#28 ;设定计数次数50,作为中断次数计数的全局变量
SETB EA ;开中断
SETB ET0 ;允许T0中断
SETB TR0 ;启动定时器0
此部分也需要放到MAIN函数中
显示程序
SHOW:
LCALL LOOSE ;调用子程序拆分BCD码并存入显示缓冲区
LCALL DISP ;调用显示子程序进行显示
LJMP SHOW
DISP
有6位数需要显示,所以需要循环六次,故把 R3 置 6 然后使用 DJNZ 循环
我们不需要 2,5 两位数码管显示数字
所以循环中使用index去取得位码,位码的值已经在 40-45H 单元中设置好了
这里为了同时显示数字和"-"所以把它们两个分开了
使用index也是因为这个原因,不然的话位码缓存位只需要一个地址位就够了
这里调用了一下 Delay 是为了防止抖动,不调用的话会显示错误,
但是放到中断循环中又太快了,所以出此下策
DISP: ; 显示子程序
MOV R3,#6H ; 总共6个位
MOV index,#00H ; index置0
M1:
LCALL Delay
MOV A,#40H
ADD A,index
MOV R0,A
MOV A,@R0
MOV P2,A
MOV DPTR,#DispTabLe
MOV A,#20H ;A地址指向缓存
ADD A,index ;位码的值加到A上
MOV R0,A
MOV A,@R0
MOVC A,@A+DPTR ;取段码
MOV P1,A
INC index ;index 加一
DJNZ R3,M1
M2: ;显示两个"-"的子程序
LCALL Delay
MOV P2,#02H ; 02和05
MOV P1,#40H ; "-"的段码为 #40H
LCALL Delay
MOV P2,#05H
MOV P1,#40H
RET
BCD转换
这里为了实现60进制所以把 30-32H 作为 十进制数 的缓存地址
取出十进制数后分别取高低位然后送入20-25H 的6位时间缓冲区
LOOSE: ;拆分子程序,压缩BCD码->hex
MOV R5,#03H ;总共3个字节,需要循环3次
MOV R0,#30H ;时间序列存储首地址
MOV R1,#20H ;6位缓冲区
LOOP1:
MOV A,@R0
ANL A,#0F0H ;取高位
SWAP A
MOV @R1,A
INC R1
MOV A,@R0 ;存高位
ANL A,#0FH ;取低位
MOV @R1,A ;存低位
INC R0
INC R1
DJNZ R5,LOOP1
RET
秒数自增
BCD转换完成后可以使用 CJNE 命令来实现进位
看注释即可明白,不过多叙述
NUMINC: ;时间序列加一秒子程序
MOV R1,#32H ;时间序列末地址(秒)
MOV A,@R1
ADD A,#01H ;加1
DA A ;转BCD码
MOV @R1,A ;保存
CPL P3.0
CJNE @R1,#60H,TORET ;若未到60秒,直接返回,否则执行下面程序
MOV @R1,#00H ;秒数清零
DEC R1 ;进行分钟位的操作
MOV A,@R1
ADD A,#01H
DA A
MOV @R1,A
CJNE @R1,#60H,TORET ;若未到60分,直接返回,否则执行下面程序
MOV @R1,#00H ;分钟数清零
DEC R1 ;进行时数的操作
MOV A,@R1
ADD A,#01H
DA A
MOV @R1,A
CJNE @R1,#24H,TORET ;若未到24时,直接返回,否则执行下面程序
MOV @R1,#00H ;小时数清零
完整源代码
汇编源码
ORG 0000H
SJMP MAIN
ORG 000BH
LJMP SER0
cDisplayBuffer EQU 20H ;6位段码实际值的缓冲区
cDisplayBit EQU 40H ;6位位码的缓冲区 40-45;47 46固定显示"-"
index EQU 50H
MAIN:
MOV index,#00H
MOV cDisplayBit,#00H
MOV cDisplayBit+1,#01H
MOV cDisplayBit+2,#03H
MOV cDisplayBit+3,#04H
MOV cDisplayBit+4,#06H
MOV cDisplayBit+5,#07H
; 存在2 5两位需要显示为 - 所以选用六个内存空间作为位码缓存
MOV SP,#70H
CLR EA
MOV 30H,#22H ;设置初始时间值
MOV 31H,#59H
MOV 32H,#55H
MOV TMOD,#01H
MOV TH0,000H
MOV TL0,0D8H
MOV R6,#28
SETB EA
SETB ET0
SETB TR0
SHOW:
LCALL LOOSE
LCALL DISP
LJMP SHOW
SER0:
MOV TH0,#000H
MOV TL0,#0D8H
DJNZ R6,EXIT
MOV R6,#28
LCALL NUMINC
EXIT:
RETI
LOOSE:
MOV R5,#03H ;总共3个字节,需要循环3次
MOV R0,#30H ;时间序列存储首地址
MOV R1,#20H ;6位缓冲区
LOOP1:
MOV A,@R0
ANL A,#0F0H ;取高位
SWAP A
MOV @R1,A
INC R1
MOV A,@R0 ;存高位
ANL A,#0FH ;取低位
MOV @R1,A ;存低位
INC R0
INC R1
DJNZ R5,LOOP1
RET
DISP: ; 显示子程序
MOV R3,#6H ; 总共6个位
MOV index,#00H ; index置0
M1:
LCALL Delay
MOV A,#40H
ADD A,index
MOV R0,A
MOV A,@R0
MOV P2,A
MOV DPTR,#DispTabLe
MOV A,#20H ;A地址指向缓存
ADD A,index ;位码的值加到A上
MOV R0,A
MOV A,@R0
MOVC A,@A+DPTR ;取段码
MOV P1,A
INC index ;index 加一
DJNZ R3,M1
M2: ;显示两个"-"的子程序
LCALL Delay
MOV P2,#02H ; 02和05
MOV P1,#40H ; "-"的段码为 #40H
LCALL Delay
MOV P2,#05H
MOV P1,#40H
RET
NUMINC:
MOV R1,#32H ;时间序列末地址(秒)
MOV A,@R1
ADD A,#01H ;加1
DA A ;转BCD码
MOV @R1,A ;保存
CPL P3.0
CJNE @R1,#60H,TORET
MOV @R1,#00H
DEC R1
MOV A,@R1
ADD A,#01H
DA A
MOV @R1,A
CJNE @R1,#60H,TORET
MOV @R1,#00H
DEC R1
MOV A,@R1
ADD A,#01H
DA A
MOV @R1,A
CJNE @R1,#24H,TORET
MOV @R1,#00H
TORET:
RET
DispTable: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH
Delay:
MOV R0,#10
MOV R1,#10
DJNZ R1,$
DJNZ R0,$-4
RET
END
原理图
这里注意区分一下自己单片机的段码位码输出IO,前面已经说过了
后记
后面使用示波器抓波然后对循环和中断部分进行微调可以实现微秒级别的误差
这里我使用了CPL输出所以T=2s
“-” 的显示上还有其他方案,望读者自行思考
同学有的使用不判断进位来实现 “-” 的显示,这样displaybuffer仅需1位
不必像本文这样冗杂
希望读者能够有所收获!文章来源:https://www.toymoban.com/news/detail-403250.html
参考了这篇文章
基于MCS-51单片机使用定时器编写时钟程序(汇编)
数码管显示部分在我的另一篇文章中有更详细的讲解
51单片机出租车计价器(汇编语言)
仿真文件下载 传送门
文章允许规范转载文章来源地址https://www.toymoban.com/news/detail-403250.html
到了这里,关于51单片机8位数码管计时器(汇编语言)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!