RYMCU

51单片机第8章--数码管实验

Hugh 4 年前
# 8段数码管 # 51单片机

所属作品集

8.1 八段数码管简介

在我们生活中随处可见数码管的应用,数码管的应用形式多种多样,抛开事物表象,深入到它的本质,正所谓万变不离其中,数码管应用的原理都是基本相通的。本章我们将详细介绍数码管的基本原理以及数码管的应用。最典型的数码管为 8 段 LED 数码管,外观如下图所示:
图8-1-8段数码管外观图.png
如上图所示,数码管包括 A、B、C、D、E、F、G 以及 DP 共 8 段,实际上 8 段为 8 个独立的 LED。上面 8 段 LED 组成 1 位数码管。数码管按照内部连接方法的不同分为共阴型或共阳,当 8 段 LED 的阴极连接在一起称为为共阴型,阳极连接在一起称为共阳型。如下图所示为 4 位共阳型的 8 段数码管。
图8-2-4位共阳极8段数码管.png
如上图所示,4 位数码管每一位的 8 段 LED 的阳极连在一起,组成共阳极型数码管,8 段数码管的阴极分别连接在一起。当选中某一位的阳极使其为高电平,使 8 段数码管的阴极对应的位低电平便可点亮数码管。例如,DP、G、F、E、D、C、B、A 电平值依次为:1、1、1、1、1、0、0、1 时数码管显示为数字 1。据此可以编辑共阳数码管的真值表,如下表所示:
表8-1-共阳数码管真值表.png
一般点亮一段数码管需要至少 10mA, 而单片机管脚输出的电流较小,无法驱动数码管,可以采用上拉电阻、三极管驱动或采用专用驱动芯片进行驱动。RY-51 单片机开发板采用两个 74HC573 驱动芯片,驱动 2 个 4 位的数码管,共 8 位数码管如下图所示:
图8-3-8位数码管设计原理图.png

8.2 锁存器 74HC573 功能介绍

如上图所示,U3,U4 为 8 位的数据锁存器 74HC573,D7-D0 为数据输入端,Q7-Q0 为与输入端一一对应的输出端。74HC573 有 2 个控制信号分别为第 1、11 管脚、1 管脚为芯片使能控制端,11 管脚为数据锁存控制端。

当 1 接高电平时,芯片不工作,无论数据输入端为高、低电平,输出端 Q7-Q0 为高阻态。只有当 1 管脚为低电平时,芯片才能正常工作,如图 8-3 所示 U3、U4 均将 1 管脚直接接地。

在 1 管脚接地的情况下,当 11 管脚为高电平时,D7-D0 输入为高电平时,Q7-Q0 输出为高、同理当输入为低电平时,输出为低电平,此时相当于芯片的 D7-D0 与 Q7-Q0 是一一连通的。

当 11 管脚为低电平时,无论输入端 D7-D0 是高或者低电平,Q7-Q0 保持原来的值不变。

利用上面的特点我们就可以实现数据的锁存了。假设我们要把 0xFB 锁存到 Q7-Q0,首先使 11 管脚为高电平,给输入端 D7-D0 赋值 0xFB,此时输出端 Q7-Q0 输出为 0xFB,此时将 11 管脚拉为低电平并一直保持,那么此后无论输入端 D7-D0 为何值,Q7-Q0 均为 0xFB,因此完成了数据的锁存。74HC573 真值表如下表所示:
表8-2真值表.png

如上面原理图所示,根据锁存器 74HC573 的特点,利用单片的一个 8 位端口,以及两个用于控制的 I/O 口便能控制 8 位数码管了。如图所示,输入数据 D7-D0 与单片的 P0 口相连,U3、U4 的 11 管脚分别与单片机的 I/O 口 P2.7、P2.6 相连。锁存器 U3 的输出端 Q7~Q0 分别与数码管的 a-g,DP 相连,锁存器 U4 的输出端 Q7-Q0 分别与 8 位数码管的公共端相连。

8.3 单片机控制数码管

下面我们通过举例来讲解数码管的控制过程,以使第 5 位数码管显示数字 8 为例。如表 8-1 所示数字 8 对应数码管真值为 0x80,通过单片机的 P0 口将 0x80 锁存到 U3 的输出口。如图 8-3 所示,第 5 位数码管公共端 WE5 对应的是锁存器 U4 的 Q4,应只需 Q4 为高电平,Q7-Q5,Q3-Q1 为低电平,便可使第 5 位数码管显示数字 8,其它数码管不显示。因此通过单片机的 P0 口将 0x10 锁存到 U4。数码管的显示总结如下,首先将字码锁存到 U3,然后将数码管的显示位锁存到 U4。数码管显示程序如下所示:

/*----------------------------------------------------
** 数码管5显示数字8
----------------------------------------------------*/
#include<reg52.h>

sbit DU = P2^7;
sbit WE = P2^6;

void main()
{
	P0 = 0x80;//显示数字8
	DU = 1;
	DU = 0;

	P0 = 0x10;//第5位数码管
	WE = 1;
	WE = 0;

	while(1);
}

程序代码如上所示,在主程序中首先把数字 8 锁存到锁存器 U3,然后将数码管 5 公共端的高电平锁存到 U4,将程序编译并下载到单片机检验实际显示效果。每一个数字对应数码管都是固定的,在实际应用中每条语句中都对 P0 进行赋值的话会比较繁琐而且不便于程序阅读,我们往往将数码管的真值放在一个数组中,程序中调用数组即可,改进程序如下所示:

/*----------------------------------------------------
** 数码管5显示数字8,字码组放入数组中
----------------------------------------------------*/
#include<reg52.h>

#define uchar  unsigned char
#define uint unsigned int

sbit DU = P2^7;
sbit WE = P2^6;

//共阳型(0~9,A,b,C,d,E,F,全亮,全灭),字码组
uchar code table_D[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0x00,0xFF};
//位选数组
uchar code table_W[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0xFF,0x00};

void main()
{
	
	P0 = table_D[8];//显示数字8
	DU = 1;
	DU = 0;

	P0 = table_W[4];//第5位数码管
	WE = 1;
	WE = 0;

	while(1);
}

如代码所示,将数码管真值表放入数组 table_D[],数组的第 0 位对应的数字为 0,第 8 位对应的数字为 8,因此,如代码所示,需要显示数字 8,直接将 table_D[8],赋值给 P0 即可,方便又好记。Table_W[]设计的原理相同。将程序下载到开发板,显示效果与前面保持一致。

前面讲解的都是让某一各数码管显示某一个固定的值,下面我们讲解较为复杂一点数码管显示。例如,第 1 位数码管显示数字 0,500ms 之后第 2 位数码管显示数字 1,直到第 8 位数码管显示 7,然后循环上述步骤。要实现上面的功能我们需要用到前面讲解的定时器功能,即每 500ms 在定时器中断程序中给数码管更新一次数值。程序代码如下所示。

/*----------------------------------------------------
** 数码管1~8循环显示数字0~7,间隔500ms
** 定时器中断方式
----------------------------------------------------*/
#include<reg52.h>

#define uchar  unsigned char
#define uint unsigned int

#define FOSC 11059200 //单片机晶振频率
#define T_1ms (65536 - FOSC/12/1000)  //定时器初始值计算

uint count = 0;
uint flag  = 0;

sbit DU = P2^7;
sbit WE = P2^6;
//共阳型(0~9,A,b,C,d,E,F,全亮,全灭),字码组
uchar code table_D[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0x00,0xFF};
//位选数组
uchar code table_W[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0xFF,0x00};
void main()
{
	TMOD = 0x01;	 //定时器工作模式配置
	TL0  = T_1ms;	//装载初始值
	TH0  = T_1ms>>8;

	TR0  = 1;		 //启动定时器
	ET0  = 1;		 //允许定时器中断
	EA   = 1;		 //开总中断

	while(1);	  //循环
}

void timer0() interrupt 1
{
	TL0 = T_1ms;//重装初始值
	TH0 = T_1ms>>8;

	count++;
	if(count>= 500)//每一毫秒进入一次中断,达到500次则为500ms,跟新一次数码管显示。
	{
		count = 0;

		P0 = table_D[flag];//数字0~7循环
		DU = 1;
		DU = 0;
	
		P0 = table_W[flag];//数码管1~8循环点亮
		WE = 1;
		WE = 0;

		flag++;
		if(flag>=8)
			flag =0;
	}
}

将程序下载到单片机开发板检验效果是否与预期一致。

8.4 动态显示数码管

前面讲解的数码管显示均为在某个时间内只能显示一个数码管,那么如何让 8 各数码管同时显示不同的数。前面我们讲解的例子为数码管 1 到 8 分别显示数字 1 到 8,显示的间隔时间为 500ms,如果我们将时间间隔该为 100ms,那么数码管切换的速度比以前快了 5 倍,当把间隔该为 2ms 时,由于间隔时间太短,感觉前一个数码管还没来的及完全熄灭,后面的数码管就点亮了,这就是人眼睛的视觉暂留效应。

因此,你可以看到数码管上同时显示了 01234567 共八个数,这就是动态数码管显示的原理。只需要将上面的程序稍加改动便可实现,将语句“if(count>=500)”改为“if(count>=2)”。将修改好的程序编译下载到开发板观察效果。

8.5 数码管消隐

当把上面的程序下载到开发板时,8 位数码管会同时显示“01234567”共 8 个数字。但是,细心的同学会发现,有的数码管不应该显示的段会有点亮,但亮度会比较低,看起来像阴影一样。这种现象是怎么形成的呢,又该怎么消除呢?下面我们通过程序来具体分析。
消隐.png

8.4 节所讲的定时器中断程序如上图所示,根据前面介绍可知,每隔 2ms 语句 51-59 会执行一次,每次 flag 的值会加 1。第一次时 flag=0,数码管 1 显示数字 0,第二次时,flag=1,数码管 2 显示数字 1,如此循环下去。当第二次执行到第 51 行语句时,此时还没有选择数码管 2,而是第一次执行语句 57 时选中了的数码管 1,只有当语句执行到 57 时,才完成数码管 2 的选定,因此,在执行语句 51-57 期间,是数码管 1 显示了数字 1,当执行到 57 之后的 2ms 时间内是数码管 2 显示了数字 1。由于语句执行语句 51-57 的时间远小于 2m,因此亮度较低,这就是我们上面观察到的阴影。阴影产生的原因找到了,下面我们讲解阴影的消除了。如果我们在语句 51 之前先把所有的数码关都关掉,就不会出现上述情况了。程序如下:

void timer0() interrupt 1
{
	TL0 = T_1ms;//重装初始值
	TH0 = T_1ms>>8;

	count++;
	if(count>= 2)//每一毫秒进入一次中断,达到2次则为2ms更新一次数码管。
	{
		count = 0;

		P0 =table_W[9];//关闭所有数码管,消隐处理
		WE = 1;
		WE = 0;

		P0 = table_D[flag];//数字0~7循环
		DU = 1;
		DU = 0;
	
		P0 = table_W[flag];//数码管1~8循环点亮
		WE = 1;
		WE = 0;

		flag++;
		if(flag>=8)
			flag =0;
	}
}

将程序重新编译,下载到开发板。正如程序设计所愿,数码管阴影成功被消除。

8.6 数码管应用

这里我们在扩展一下数码管的应用,做一个简单秒表,在数码管上显示。程序设计原理如下:在定时器中断程序中增加一个变量 T_count,当 1ms 进入一次定时器中断时自加一次,然后在主程序中判断是否达到了 1000 次即 1s 的时间,每秒钟变量 Sec 自加 1 次,记录时间。将 sec 个、十、百、千位保存到数组 Buf_LED[]中,在将数组显示到数码管上,程序如下所示:

/*----------------------------------------------------
** 秒表
----------------------------------------------------*/
#include<reg52.h>

#define uchar  unsigned char
#define uint unsigned int

#define FOSC 11059200 //单片机晶振频率
#define T_1ms (65536 - FOSC/12/1000)  //定时器初始值计算

uint count = 0;
uint flag  = 0;

uint T_count  = 0;
uint Sec   = 0;

sbit DU = P2^7;
sbit WE = P2^6;	

uchar  Buf_LED[8] ={0};


//共阳型(0~9,A,b,C,d,E,F,全亮,全灭),字码组
uchar code table_D[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0x00,0xFF};
//位选数组
uchar code table_W[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0xFF,0x00};

void main()
{
	TMOD = 0x01;	 //定时器工作模式配置
	TL0  = T_1ms;	//装载初始值
	TH0  = T_1ms>>8;

	TR0  = 1;		 //启动定时器
	ET0  = 1;		 //允许定时器中断
	EA   = 1;		 //开总中断

	while(1)	  //循环
	{
		if(T_count>=1000)
		{
			T_count =0;
			Sec++;
		
		Buf_LED[7]= Sec%10;
		Buf_LED[6]= Sec/10%10;
		Buf_LED[5]= Sec/100%10;
		Buf_LED[4]= Sec/1000%10;
		}
	}
}

void timer0() interrupt 1
{
	TL0 = T_1ms;//重装初始值
	TH0 = T_1ms>>8;
	
	T_count++;
	count++;
	if(count>= 2)//每一毫秒进入一次中断,达到2次则为2ms更新一次数码管。
	{
		count = 0;

		P0 =table_W[9];//关闭所有数码管,消隐处理
		WE = 1;
		WE = 0;

		P0 = table_D[Buf_LED[flag]];//显示秒
		DU = 1;
		DU = 0;
	
		P0 = table_W[flag];//数码管1~8循环点亮
		WE = 1;
		WE = 0;

		flag++;
		if(flag>=8)
			flag =0;
	}
}

8.7 本章小结

本章详细介绍了数码管的工作原理、锁存器 74HC573 的工作原理及使用。数码管的显示控制以数码管显示中的定时器应用。

所属作品集

后发布评论