I/O输出——实例3:数码管动态扫描显示
I/O输出——实例3:数码管动态扫描显示
一、实例目的
通过本实例,我们应:
1. 了解多位数码管的结构及引脚;
2. 了解多位数码管驱动电路;
3.掌握数码管动态扫描程序设计。
二、多位数码管结构及引脚
多位数码管用于显示更多的数值信息,其由多个数码管组成,这些数码管的段(a~h)一一对应连接在一起,公共极独立。4位数码管外观如图1所示。
图1 4位数码管
注:图1来源自Wikipedia,原文链接:https://en.wikipedia.org/wiki/Seven-segment_display
四位数码管引脚见表1。
表1 四位数码管引脚
引脚 |
功能 |
a~h |
多位数码管的段控制端,输入,决定数码管的显示内容 |
com0~com3 |
数码管的公共端,即共阴极或共阳极,又称位选
|
二、多位数码管驱动原理
因为多位数码管的a~h段在内部连接在一起,为了让数码管显示不同的内容,只能采用动态扫描方式,即按照一定方向(例如从左到右)逐一选中数码管,单片机输出相应的显示数据,并保持一定的时间,如此循环。由于人眼的视觉暂留效应,我们看到多位显示内容是同时显示的,这与动画由一幅幅图片动态快速切换的原理是类似的。 根据经验,当帧率为15fps以上,则动画是连贯的(一帧就是一幅图像)。帧率越高动画越流畅,一般动画要求帧数在24帧以上。对于四位数码管,显示四位数字为一帧,按25fps计算,则每位数字的显示时间为10ms左右(1s/25/4)(帧率取25fps,4为数码管的位数)。但是,经过实测,每位数字显示10ms,明显看到辉光闪烁,刷新频率不够,改为5ms则稳定显示。
8位数码管的驱动电路如图2所示。一个74HC573锁存器用于提供数码管驱动电流,一个74LS138译码器用于片选8位数码管,任意时刻仅有一个数码管是选中的。
图2 8位数码管动态扫描驱动电路
下面简单介绍74HC573和74LS138芯片的功能和引脚。
74HC573是8位锁存器(时序电路,输出不仅仅取决于输入,还与电路之前的状态有关,具有记忆(存储)功能),用于锁存8位数据,其引脚见表2。
表2 74HC573引脚
引脚 |
功能 |
D0~D7 |
输入,8位待锁存的数据 |
Q0~Q7 |
输出,三态,内部8位锁存器的输出
提示:三态指高电平、低电平和高阻态。高阻态相当于与其它电路断开。通常总线(地址线、数据线都是三态的)
|
输入,输出使能,低电平有效
输入高电平时,Q0~Q7呈高阻态;输入低电平时,Q0~Q7为内部8位锁存器输出
提示:如果管脚带帽(名称上有一个横线),表示该引脚低电平有效,否则为高电平有效。
|
|
LE |
输入,锁存使能端,高电平有效。
LE = 1时,D0~D7能够进入到内部的8位锁存器
LE = 0时,内部的8位锁存器保持,即D0~D7无法进入到锁存器内部
|
74LS138是38译码器(组合电路,输出由输入决定,无记忆(存储)功能),其引脚见表3,功能表见表4。
表3 74LS138引脚
引脚 |
功能 |
C,B,A | 输入,译码输入,C为最高位,A为最低位,共8种状态 |
Y0~Y7 |
输出,38译码器输出,低电平有效
提示:如果管脚后有一个圆圈。,表示该引脚有效时,输出低电平。
|
E1 | 输入,使能端,高电平有效 |
E2、E3 |
输入,使能端,低电平有效
提示:如果管脚前有一个圆圈。且引脚名称未带帽(—),表示该引脚低电平有效,否则为高电平有效。
|
表4 74LS138引脚功能表
注: G2=E2+E3, G1=E1
三、多位数码管动态扫描程序设计
下面以使用4位数码管显示“2016”为例进行介绍。
动态扫描程序主流程图如图3所示。
图3 4位数码管动态扫描显示流程图
采用结构式编程,将数码管动态扫描相关的函数写在一个7seg.h头文件里,便于移植和嵌入。当工程用到数码管动态扫描时,仅需引用7seg.h和修改相关的硬件接口定义即可。
- 新建一个工程LEDDynamic。
- 新建两个文件,一个main.c,一个7seg.h,添加到本工程。文件内容如下:
//main.c
#include"7seg.h"
//显示数据存在一个数组里
unsigned char num[] = {2,0,1,6};
/****************************
*函数:delayMS
*功能:ms级延时函数@12MHz主频
*参数:ms:unsinged int,延时时间,单位:ms
*返回值:无
*****************************/
void delayMS(unsigned int ms)
{
unsigned int i,j;
for(i=0;i<ms;i++)
for(j=0;j<150;j++);
}
//main是主程序
void main()
{
unsigned char i;
while(1)
{
for(i=0;i<4;i++) //4位数码管,逐个扫描,共四次
{
led_display(i,num[i]); //显示2016
delayMS(10);
}
}
//7seg.h
#include"reg51.h"
#define led_seg P0 //段控制a~h
#define led_com P2 //P2.2~P2.0控制74LS138译码的输入(C,B,A)
//共阴极段码表code table for 0-9,A-F, and off
unsigned char LED_CODE[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,
0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};
/****************************
*函数:led_display
*功能:片选某位数码管,显示指定数字
*参数:com:unsigned char,数码管位选;number:unsigned char,显示的数字
*返回值:无
*****************************/
void led_display(unsigned char com, unsigned char number)
{
led_com = com;
led_seg = LED_CODE[number];
}
7seg.h的led_display函数即为动态扫描的函数,选中某位数码管,显示指定内容。要显示多少位,则调用多少次。注意,每位数码管显示间隔时间不能太大也不能太小,太大则显示结果出现逐位显示现象,太小可能导致每位数码管显示时间过短,亮度不够。原则上,N位数码管,每位数码管显示时间T=1s/(25*N)。
3.编译并生成.hex。
4.在Proteus进行仿真。
仿真结果如下:
图4 数码管动态扫描显示2016(每位数码管保持10ms)
图5 数码管动态扫描显示2016(每位数码管保持16ms,约15fps)
可见,当扫描间隔超过16ms时,明显看出显示不连贯。
但是,经过实测,每位数字显示10ms,明显看到辉光闪烁,刷新频率不够,改为5ms则稳定显示。 至此,数码管动态扫描驱动电路和程序已经学完。上面例子是在main函数里进行数码管动态扫描显示,如果存在其他延时(如按键延时,呼吸灯延时),则这些延时过程中,数码管只能显示最后一个扫描的数码管的内容,这是很糟糕的。有没有好的解决办法呢?当然有,改为使用定时器中断周期地去刷新显示内容。