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位
不必像本文这样冗杂
希望读者能够有所收获!

参考了这篇文章
基于MCS-51单片机使用定时器编写时钟程序(汇编)
数码管显示部分在我的另一篇文章中有更详细的讲解
51单片机出租车计价器(汇编语言)
仿真文件下载 传送门
文章允许规范转载

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐