Proteus+汇编+8086实现电子时钟 第一篇 动态扫描
摘要
本专栏介绍在Proteus软件中使用汇编语言,8086作为控制器,实现电子时钟。以下是实现功能
这是第一篇,介绍动态扫描的实现,分为原理,原理图和代码,原理解析(主要是为什么要关位码)三个部分。
动态扫描原理
动态扫描其实就是在很短的时间里快速闪过每一位数字,快到让人眼分辨不清,就实现了“同时”显示多位数字的效果。
原理图和代码
本文中采用的是共阳极数码管。
TABCODE DB 0c0h,0f9h,0a4h,0b0h,99h,92h,82h,0f8h,80h,90h ;共阳极数码管。0-9位码
ABCDEGFG送位码,DP是小数点,12345678送段码。
数据段和堆栈段定义
;数据段
DATA SEGMENT
ORG 100H
PORTA EQU 8000H
PORTB EQU 8002H
PORTC EQU 8004H
TCONTRO EQU 0A006H
TCON0 EQU 0A000H
TCON1 EQU 0A002H
A8259 EQU 0C000H
B8259 EQU 0C002H
TABCODE DB 0c0h,0f9h,0a4h,0b0h,99h,92h,82h,0f8h,80h,90h ;共阳极数码管。0-9位码
TABADD DB 80H,40H,10H,08H,02H,01H ;这里没有用所有数码管
HOUR DW 0203H;非压缩BCD数
MINUTE DW 0509H
SECOND DW 0502H
COUNT DB 00H
SELECTED DW 0000H
ALARMHOUR DW 0000H
ALARMMINUTE DW 0000H
ALARMSECOND DW 0000H
FLAG DB 00H ;为0表示不设闹钟,为1表示输入时,为2输入分,为3输入秒
DATA ENDS
;栈堆段
STACK SEGMENT
STA DB 1000H DUP(0)
TOP EQU LENGTH STA
STACK ENDS
END START
延时函数DELAY
;短延时函数,用于正常显示
DELAY:
PUSH CX
MOV CX,000Ah
LOOP $
POP CX
RET
;长延时函数,用于闪烁
DELAY2:
PUSH CX
MOV CX,0200H
LOOP $
POP CX
RET
这里的CX数值选择很重要,可以多尝试一下,测试下来CX=000AH比较合适。
(这里在每一位输出之后有一个判断,决定是否去调用DELAY2,是我想根据标志位决定是否让时分秒实现类似闪烁的功能,可以先忽略)
SHOW:
LEA BX, TABCODE ;用于动态扫描显示当前时间,这里如果想简化代码,可以在data segment hour前加address equ $,可以循环显示,但可读性较差,没有采用
MOV DX,PORTB;送段码
MOV AL,[TABADD][5]
OUT DX,AL
MOV DX,PORTA;送位码
MOV AX, HOUR
MOV AL,AH
XLAT
OUT DX,AL
CMP COUNT,01H
JZ FLICKERHOUR1
CALL DELAY;延时
RETURNHOUR1:
MOV DX,PORTA;关位码
MOV AL,0ffH
OUT DX,AL
MOV DX,PORTB;送段码
MOV AL,[TABADD][4]
OUT DX,AL
MOV DX,PORTA;送位码
MOV AX, HOUR
XLAT
OUT DX,AL
CMP COUNT,01H
JZ FLICKERHOUR2
CALL DELAY;延时
RETURNHOUR2:
MOV DX,PORTA;关位码
MOV AL,0FFH
OUT DX,AL
MOV DX,PORTB;送段码
MOV AL,[TABADD][3]
OUT DX,AL
MOV DX,PORTA;送位码
MOV AX, MINUTE
MOV AL,AH
XLAT
OUT DX,AL
CMP COUNT,02H
JZ FLICKERMIN1
CALL DELAY;延时
RETURNMIN1:
MOV DX,PORTA;关位码
MOV AL,0FFH
OUT DX,AL
MOV DX,PORTB;送段码
MOV AL,[TABADD][2]
OUT DX,AL
MOV DX,PORTA;送位码
MOV AX, MINUTE
XLAT
OUT DX,AL
CMP COUNT,02H
JZ FLICKERMIN2
CALL DELAY;延时
RETURNMIN2:
MOV DX,PORTA;关位码
MOV AL,0FFH
OUT DX,AL
MOV DX,PORTB;送段码
MOV AL,[TABADD][1]
OUT DX,AL
MOV DX,PORTA;送位码
MOV AX, SECOND
MOV AL,AH
XLAT
OUT DX,AL
CMP COUNT,03H
JZ FLICKERSEC1
CALL DELAY;延时
RETURNSEC1:
MOV DX,PORTA;关位码
MOV AL,0FFH
OUT DX,AL
MOV DX,PORTB;送段码
MOV AL,[TABADD][0]
OUT DX,AL
MOV DX,PORTA;送位码
MOV AX, SECOND
XLAT
OUT DX,AL
CMP COUNT,03H
JZ FLICKERSEC2
CALL DELAY;延时
RETURNSEC2:
MOV DX,PORTA;关位码
MOV AL,0FFH
OUT DX,AL
JMP DONESHOW
FLICKERHOUR1:
CALL DELAY2
JMP RETURNHOUR1
FLICKERHOUR2:
CALL DELAY2
JMP RETURNHOUR2
FLICKERMIN1:
CALL DELAY2
JMP RETURNMIN1
FLICKERMIN2:
CALL DELAY2
JMP RETURNMIN2
FLICKERSEC1:
CALL DELAY2
JMP RETURNSEC1
FLICKERSEC2:
CALL DELAY2
JMP RETURNSEC2
DONESHOW: RET
原理解析
可以看到SHOW函数基本就是在依次显示每一位的数据,在主循环中重复调用SHOW函数就实现了显示效果。
LEA BX, TABCODE
MOV DX,PORTB;送段码
MOV AL,[TABADD][5]
OUT DX,AL
MOV DX,PORTA;送位码
MOV AX, HOUR
MOV AL,AH
XLAT
OUT DX,AL
CMP COUNT,01H
JZ FLICKERHOUR1
CALL DELAY;延时
RETURNHOUR1:
MOV DX,PORTA;关位码
MOV AL,0ffH
OUT DX,AL
每一位的显示分为送段码,送位码,延时,关位码四个部分。
因为使用的8255有输出锁存功能,所以可以先送段码,再送位码。
在调用延时函数后需要有关位码这一操作,否则这一位显示不了,还会影响下一位的显示
共阳极数码管关位码的操作需要送0ffH。
如果送00H这一位也显示不了
影响下一位原因在于输出锁存功能,在进行下一位的显示之前,位选信号一直是上一位的输出,如果不关位码,下一位输出会先短暂的显示上一位的输出。
至于要显示的这一位为什么显示不了,可能是输出ffh才可以让这一位最后亮一下,利用余辉效应让在显示其他位的时候这一位也能亮一会(我猜的)
总之是在非当前显示位的位选信号设置为非激活状态,通常是高电平。