一、数码管的显示原理
数码管:是一种可以显示数字和其他信息的电子设备,可以看成是多个二极管的组成。
数码管有单位数码管、双位数码管、四位数码管,我们开发板上的数码管为2个四位数码管。
数码管显示原理:通过点亮内部的发光二极管来发光
引脚:又被称为管脚,从集成电路内部电路引出与外围电路的接线,所有的引脚构成了这块芯片的接口
单位数码管的引脚有10个,内部一共有8个小的发光二级管,还有2个引脚是公共端,其实只有一个公共端,但是生产厂家为了封装统一,把这2个引脚连接在了一起。
数码管根据其公共端所接的阳极和阴极的不同,分为了共阴极数码管和共阳极数码管。
共阳极接法:几个二极管的阳极接在一起,接到VCC(高电平),我们要想点亮,只要在在对应的二极管的阴极接上低电平即可。
共阴极接法:几个二极管的阴极接在一起,接到GND(低电平),我们要想点亮,只要在在对应的二极管的阳极接上高电平即可。
我们所用开发板上的是数码管是共阴极接法,如果要要显示数字1,我们只需让“b、c”得高电平,其余引脚给低电平即可,转换为16进制表示即为0x06,下面为共阴极数码码表:
16进制表示 | 显示的数字 |
---|---|
0x3f | 0 |
0x06 | 1 |
0x5b | 2 |
0x4f | 3 |
0x66 | 4 |
0x6d | 5 |
0x7d | 6 |
0x07 | 7 |
0x7f | 8 |
0x6f | 9 |
16进制表示 | 显示的字母 |
---|---|
0x77 | A |
0x7c | B |
0x39 | C |
0x5e | D |
0x79 | E |
0x71 | F |
0x76 | H |
0x38 | L |
0x40 | - |
0x00 | 熄灭 |
STC89C52开发板上所用的是2个四位的数码管,在其内部公共端是独立的,独立的公共端可以用来控制哪一位数码管点亮,段线是连接在一起的,用来负责显示什么数字,我们常常把公共端叫做”位选线“,连接在一起的线叫做”段选线“。
位选:控制哪一个数码管亮
段选:控制数码管显示的内容
有了这两个线之后,通过单片机及外部驱动电路就可以控制任意的数码管显示任意的数字。
二、74HC573锁存器工作原理
2.1 原理图分析
OE:Output Enable 输出使能端
LE:Latch Enable 锁存器使能端
D0~D7:数据的输入引脚
Q0~Q7:数据的输出引脚
GND:地线接地端
VCC:供电电压
74HC573锁存器:是一种对脉冲电平敏感的存储单元电路,它们可以在特定输入脉冲电平作用下改变状态。锁存,就是把信号暂存以维持某种电平状态。锁存器的最主要作用是缓存,其次完成高速的控制器与慢速的外设的不同步问题,再其次是解决驱动的问题,最后是解决一个 I/O 口既能输出也能输入的问题。一个口可以控制两个LED灯,对第一个LED灯送数据,“打开”第一个锁存器,而“锁住”第二个锁存器,使得第二个LED上的数据不变。对第二个LED灯送数据时,“打开”第二个锁存器而“锁住”第一个锁存器,使得第一个LED灯上的数据不变。
从图中来看,简单来说就是用D引脚控制Q引脚,OE和LE影响D引脚控制Q引脚的结果。
2.2 真值表
L:低电平
H:高电平
D:数据输入端
Q:数据输出端
X:任意是任何状态:高电平或者低电平
Z:高阻态
74HC573有三种工作模式:
- 锁存模式:输出使能为L,锁存使能为L,此时Qx的输出被锁存,锁存的意思就是进入锁存状态之前是高就一直是高,进入锁存状态之前是是低就是低,这就是锁存器的作用。
- 跟随模式:输出使能为L,锁存使能为H,此时Qx的输出跟随Dx的输入变化而变化,此时可以认为锁存器是透明的。
- 跟随模式:输出使能为H,无论锁存使能为什么状态,Qx都为高组态,既不是高电平,也不是低电平。在这种状态下,可以多个芯片并联输出,但是只能有一个处于非高组态,否则会烧毁芯片,高组态会在后期的RS232、RS422中用到。
在实际应用中,我们往往是按照(1)OE端给上一个低电平,LE端给上一个高电平;(2)将数据从单片机的口线上输出到Dn;(3)此时给OE端一个低电平,LE端一个低电平;(4)此时我们所需要的数据就锁存在Qn上了,输入的数据再变化也不能影响到输出的数据了
三、74HC573锁存器在数码管中的应用
从图中我们可以看出,我们要驱动2片4位的数码管,也就是说驱动8位的数码管,我们采用2片的74HC573锁存器进行驱动,一片用来锁存位码,一片用来锁存段码。74HC573锁存器能够驱动几位数码管取决于使用了几片74HC573。
74HC573锁存器驱动8位数码管
- 图中我们所采取的数码管是共阴极的数码管,可以看出数码管的阳极,即a、b、c、d、e、f、g、h的引脚全部连接在一起,然后与左边的U9锁存器的数据输出端相连,U9的数据输入端连接单片机的P0口,在P0口同时加上上拉电阻。
- 图中每一个数码管对应一个位选端(WE1 WE2 WE3 WE4 WE5 WE6 WE7 WE8),与左边U8锁存器数据的数据输出端的低8位相连,U8的数据输入端也连接到单片机的P0口
- U9、U8锁存器的锁存端分别与单片机的P2.6、P2.7相连,利用单片机控制锁存器的锁存端,进而控制锁存器的数据输出,这种分时控制可以方便的控制任意数码管显示任意的数字
- 共阴极数码管在进行位选的时候,每一位(WE1 WE2 WE3 WE4 WE5 WE6 WE7 WE8)都是公共的阴极,只有输入低电平的时候才有可能被点亮,所以在进行位选的时候必须是低电平。
- 共阴极数码管在进行段选的时候,只有输入高电平,才能点亮数码管。
- 74HC573锁存器用于数码管显示的时候通常都是采用段选、位选共用同一组并口的驱动。
- 从图中我们不难发现,如果用单片机来控制数码管的显示,直接用I/O进行驱动数码管,至少需要16个引脚才能实现数码管的控制,但是利用了锁存器之后,只需要10个引脚就可以实现数码管的控制,如果数码管的数量持续增多,则对引脚的节省就更加突出。
四、实际静态数码管程序的设计
设计要求:我们假设需求是点亮第一个数码管,并让第一个数码管显示数字1,以下是我们实现的步骤:
- 点亮第一个数码管,那么别的数码管的位选就要关闭,即只打开第一个数码管的位选
- 我们先给U8锁存器的锁存端一个高电平,然后将数据从单片机的P0口直接送出到锁存器U2的数据输出端,然后再关闭U2锁存端
- 由于数码管为共阴极数码管,所以位选选通时为低电平,位选关闭时为高电平,所以只有W1端对应的数据为低电平,其他位选位置为高电平,因此P0口要输出的数据为0xfe(二进制为1111 1110)
- 位选确定了以后,再确定段选。要显示数字1,就要给b、c段对应的数据一个高电平,其他段对应的数据一个低电平,然后接着用操作U2的方法给U1的数据输出端再送一个0x06(二进制为0000 0110)
程序代码:
#include <reg52.h> //52单片机头文件
sbit WE = P2^7; //位定义 WE为标识符 代表着位选 P2^7为地址符 申明U8锁存器的锁存端
sbit DU = P2^6; //位定义 DU为标识符 代表着段选 P2^7为地址符 申明U9锁存器的锁存端
int main() //主函数
{
WE = 1; //打开U8锁存端
P0 = 0xfe; //送入位选信号 0xfe为16进制表示方法,转换为二进制为1111 1110,代表着第一个数码管被点亮
WE = 0; //关闭U8锁存端
DU = 1; //打开U9锁存端
P0 = 0x06; //送入段选信号 0x06为16进制表示方法,转换为二进制为1111 0110,代表着显示数字1
WE = 0; //关闭U9锁存端
while(1); //程序到这里停止
}
效果:
进阶显示1(联系前面所学的延时函数)
设计要求:让实验板子上的8个数码管同时点亮,依次显示数字0到F,循环下去
这里我们要引入编码定义的概念
uchar code table[] = { //这里的uchar 为unsigned char的缩写 在使用时需要先定义
0x3f 0x06 0x5b 0x4f
0x66 0x6d 0x7d 0x07
0x7f 0x6f 0x77 0x7c
0x39 0x5e 0x79 0x71};
编码定义与C语言中数组定义的方法非常类似,不同的地方在于多了一个code,code即为编码的意思。
注意:单片机C语言中定义数组时是占用内存空间的,而定义编码时是直接分配到程序空间中的,使用编码的方式进行编译,占用的是程序空间,而不是内存空间,节省了资源
[]中的当前数字个数可以加也可以不加,C51编译器在编译时能够自动计算出来,通常来说,为了方便,不用去加
调用数组的方式:
P0 = table[4];
即将table这个数组中第5个元素直接赋给P0口,即
P0 = 0x6d;
注意:在使用数组的时候,table后面中括号的数字是从0开始的,对应后面大括号里的第1个元素。
程序代码:
#include <reg51.h> //51单片机头文件
sbit WE = P2^7; //位选信号的锁存器控制 位定义 WE为标识符 代表着位选 P2^7为地址符 申明U8锁存器的锁存端
sbit DU = P2^6; //段选信号的锁存器控制 位定义 DU为标识符 代表着段选 P2^7为地址符 申明U9锁存器的锁存端
#define uchar unsigned char //宏定义(预处理指令,不是语句,后面不用加分号)其中直接用uchar替换了unsigned char 此时我们可以用uchar num等价于unsigned char num;
#define uint unsigned int //宏定义(用法和上面类似)
uchar num; //定义变量num
uchar code table[] = { //显示0~f的码表
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
void delay(uint z) //延时函数
{
unsigned int x,y;
for(x = z;x>0;x--)
for(y=144;y>0;y--);
}
void main() //主函数
{
while(1) //进入大循环
{
for(num=0;num<19;num++) //16个数循环显示
{
WE = 1; //打开U8锁存器(U8为位选)
P0 = 0x00; //送入位选信号,选中8个数码管
WE = 0; //关闭U8锁存器
delay(1000); //调用延时函数
DU = 1; //打开U9锁存器(U9为段选)
P0 = table[num]; //送入段选信号,调用数组中的值
DU = 0; //关闭U9锁存器
}
}
}
效果:(由于压缩成gif大小有限,只上传了开始和结尾的部分)
进阶显示2
设计要求:让实验板子上的数码管从第一位开始流动显示数字1
程序代码:
#include <reg51.h> //51单片机头文件
sbit DU = P2^6; //段选信号的锁存器控制 位定义 WE为标识符 代表着位选 P2^7为地址符 申明U8锁存器的锁存端
sbit WE = P2^7; //位选信号的锁存器控制 位定义 DU为标识符 代表着段选 P2^7为地址符 申明U9锁存器的锁存端
#define uchar unsigned char //宏定义(预处理指令,不是语句,后面不用加分号)其中直接用uchar替换了unsigned char 此时我们可以用uchar num等价于unsigned char num;
#define uint unsigned int //宏定义(用法和上面类似)
unsigned char code table[]={ //数码管各位的码表
0xfe,0xfd,0xfb,0xf7,
0xef,0xdf,0xbf,0x7f};
void delay(uint z) //延时函数
{
unsigned int x,y;
for(x = z;x>0;x--)
for(y=144;y>0;y--);
}
void main()
{
uchar num;
while(1)
{
for(num=0;num<8;num++)
{
WE = 1; //打开U8锁存器(U8为位选)
P0=table[num]; //送入位选信号,调用数组
WE = 0; //关闭U8锁存器
DU = 1; //打开U9锁存器(U9为段选)
P0=0x06; //送入段选信号,显示数字1的代码
DU = 0; //关闭U9锁存器
delay(200); //调用延时函数
}
}
}
效果: