51单片机8位数码管时钟
作业要求
使用8位数码管实现时间的显示,进位等操作
最终效果
理论基础
中断
实现计时器需要使用中断来完成延时 如果使用循环延迟的话,在计时过程中处理器不能进行其他操作
位码
设置具体的显示位,如 00H 是数码管的第一位 本例中使用 P2 引脚
段码
输出图形的控制 本例中使用 P1 引脚
如 40H 为 -
显示
一个数字的显示需要位码和段码同时起作用 如 P1 输入 #00H, P2 输入 #40H 在数码管的第一位会显示 “-”
这里显示需要不连续的进行显示 (数字和"-"需要分开) 所以只用一个地址位去缓存位码是不够的 我这里采用了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 ;小时数清零
完整源代码
汇编源码
作业尚未提交,暂不放出
原理图
这里注意区分一下自己单片机的段码位码输出IO,前面已经说过了
后记
后面使用示波器抓波然后对循环和中断部分进行微调可以实现微秒级别的误差 这里我使用了CPL输出所以T=2s
需要Proteus工程文件的可以私信我
|