我们先来看一下蓝桥杯板中数码管部分的电路图:

        对于动态数码管的控制,是需要有段选和位选的,位选是控制数码管哪一位显示,段选是控制该位显示什么数字,由上图可知,当Y6C有效时,P0控制的是数码管的位;当Y7C有效时,可以给P0写入显示数字的段码。以下依次是数码管0~F以及熄灭的段码:

{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8, 0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xff}

让8位数码管依次显示1 2 3 4 5 6 7 8

思路:先使Y6C有效,进行数码管显示位的选择;再使Y7C有效,为数码管输入段码。由于动态数码管显示依靠的是视觉暂留,所以数码管的每一位设定完成后,要加一个延时函数。延时程序的作用是保持当前显示数码管足够时间,同时稳定显示效果,以形成视觉暂留。延时时间长短视具体情况而定,一般要大于2毫秒,所有数码管一次扫描完成总时间不能大于40毫秒。这里我数码管每位显示后设置了一个2ms的延时(延时1ms显示也是正常的,但3ms的时候显示的数字会闪烁,这里大家可以自己设置不同时间看一下效果)

接下来我们通过代码来看一下具体该如何实现:

#include"reg51.h"
#include"intrins.h"
//数码管段码表
unsigned char code SMG_DuanMa[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
																 0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xff};
												
 
void Init_74HC138(unsigned char n)
{
	switch(n)
	{
		case 4://Y4有效,P0直接控制LED
			P2=(P2 & 0x1f) | 0x80;
			break;
		case 5://Y5有效,P0控制蜂鸣器、继电器
			P2=(P2 & 0x1f) | 0xa0;
			break;
		case 6://Y6有效,P0控制数码管位选
			P2=(P2 & 0x1f) | 0xc0;
			break;
		case 7://Y7有效,P0控制数码管段选
			P2=(P2 & 0x1f) | 0xe0;
			break;
	}
}
void Delay2ms()		//2ms的延时函数(视觉暂留)
{
	unsigned char i, j;
	_nop_();
	_nop_();
	i = 22;
	j = 128;
	do
	{
		while (--j);
	} while (--i);
}
 
void SMG_Disp()
{	
//*******第一位显示1*************
	Init_74HC138(6);  //Y6C有效,P0控制显示哪一位
	P0=0x01;          //P0=0000 0001,选通第一位(最左边一位)
	Init_74HC138(7);  //Y7C有效,P0控制显示哪一个数字
	P0=SMG_DuanMa[1];	//给P0输入数字1的段码,让数码管显示1	
	Delay2ms();
//*******第二位显示2*************	
	Init_74HC138(6);	//Y6C有效
	P0=0x02;          //P0=0000 0010,选通第二位
	Init_74HC138(7);	//Y7C有效
	P0=SMG_DuanMa[2];	//给P0输入数字2的段码,让数码管显示2		
	Delay2ms();
//*******第三位显示3*************		
	Init_74HC138(6);	//Y6C有效
	P0=0x04;					//P0=0000 0100,选通第三位
	Init_74HC138(7);	//Y7C有效
	P0=SMG_DuanMa[3];	//给P0输入数字3的段码,让数码管显示3
	Delay2ms();
//*******第四位显示4*************		
	Init_74HC138(6);
	P0=0x08;					//P0=0000 1000,选通第四位
	Init_74HC138(7);
	P0=SMG_DuanMa[4];	//数码管显示4
	Delay2ms();
//*******第五位显示*************		
	Init_74HC138(6);
	P0=0x10;					//P0=0001 0000,选通第五位
	Init_74HC138(7);
	P0=SMG_DuanMa[5];	//数码管显示5
	Delay2ms();
//*******第六位显示6*************		
	Init_74HC138(6);
	P0=0x20;					//P0=0010 0000,选通第六位
	Init_74HC138(7);
	P0=SMG_DuanMa[6];	//数码管显示6	
	Delay2ms();
//*******第七位显示7*************		
	Init_74HC138(6);
	P0=0x40;					//P0=0100 0000,选通第七位
	Init_74HC138(7);
	P0=SMG_DuanMa[7];	//数码管显示7	
	Delay2ms();
//*******第八位显示8*************		
	Init_74HC138(6);
	P0=0x80;					//P0=1000 0000,选通第八位
	Init_74HC138(7);
	P0=SMG_DuanMa[8];	//数码管显示8	
	Delay2ms();	
}
void main()
{
	while(1)
	{
		SMG_Disp();//调用数码管显示子函数
	}
}

        上述代码中的数码管显示函数SMG_Disp()是不是感觉代码稍微有点长,而且基本都是重复的语句,在对数码管显示原理搞懂之后,我们可以对这个函数进行修改,大家看下面这个函数,实现的功能一样,是不是精简了许多呢?

#include"reg51.h"
#include"intrins.h"
//数码管段码表
unsigned char code SMG_DuanMa[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
																 0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xff};
 
void Init_74HC138(unsigned char n)
{
	switch(n)
	{
		case 4://Y4有效,P0直接控制LED
			P2=(P2 & 0x1f) | 0x80;
			break;
		case 5://Y5有效,P0控制蜂鸣器、继电器
			P2=(P2 & 0x1f) | 0xa0;
			break;
		case 6://Y6有效,P0控制数码管位选
			P2=(P2 & 0x1f) | 0xc0;
			break;
		case 7://Y7有效,P0控制数码管段选
			P2=(P2 & 0x1f) | 0xe0;
			break;
	}
}
void Delay2ms()		//2ms的延时函数(视觉暂留)
{
	unsigned char i, j;
	_nop_();
	_nop_();
	i = 22;
	j = 128;
	do
	{
		while (--j);
	} while (--i);
}
//*******smg_wei代表第几位(注意这个是从0~7,0是第一位,1是第二位....)
//*******smg_val代表显示的数字
void SMG_Disp(unsigned char smg_wei,unsigned char smg_val)
{	
	Init_74HC138(6);
	P0=(0x01 << smg_wei);
	Init_74HC138(7);
	P0=SMG_DuanMa[smg_val];		
	Delay2ms();	
}
void main()
{
	while(1)
	{
		SMG_Disp(0,1);//第0位(也就是数码管的最左边的一位)显示数字1
		SMG_Disp(1,2);//第1位显示数字2
		SMG_Disp(2,3);//第2位显示数字3
		SMG_Disp(3,4);//第3位显示数字4
		SMG_Disp(4,5);//第4位显示数字5
		SMG_Disp(5,6);//第5位显示数字6
		SMG_Disp(6,7);//第6位显示数字7
		SMG_Disp(7,8);//第7位显示数字8
	}
}
 

        在SMG_Disp(unsigned char smg_wei,unsigned char smg_val)函数中,直接将数码管要显示的位和显示的数字给该函数,在该函数中用到了左移符号<<,减少了代码量。

        若想让数码管有的亮有的灭呢?因为我们用到的是共阳极数码管,只需要给P0写0xff即可。在上述代码中,我在段码表的第16位加上了0xff,这样可以直接控制该位熄灭。例如数码管前四位显示1 2 3 4 ,后四位不亮,用代码表示即为:

void main()
{
	while(1)
	{
		SMG_Disp(0,1);//第0位显示数字1
		SMG_Disp(1,2);//第1位显示数字2
		SMG_Disp(2,3);//第2位显示数字3
		SMG_Disp(3,4);//第3位显示数字4
		SMG_Disp(4,16);//第4位不显示
		SMG_Disp(5,16);//第5位不显示
		SMG_Disp(6,16);//第6位不显示
		SMG_Disp(7,16);//第7位不显示
	}
}