从零开始玩转C51之02. 数码管

02. 数码管

一.数码管静态显示

书接上回,若将单个数码管看成由8个LED组成的灯组,只需让相应位置的灯管亮起就可以实现数字的显示。
在这里插入图片描述


74HC245是方向可控的八路缓冲器,主要用于实现数据总线的双向异步通信。为了保护脆弱的主控芯片,通常在主控芯片的并行接口与外部受控设备的并行接口间添加缓冲器。当主控芯片与受控设备之间需要实现双向异步通信时,自然就得选用双向的八路缓冲器了,74HC245就是面向这种需求的。常见于同并口液晶屏、并口打印机、并口传感器或通讯模块等设备的接口上。


版权声明:本文为CSDN博主「小辉_Super」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43772810/article/details/120838688

根据普中HC6800-ES V2.0开发板原理图中动态数码管模块的器件显示,开发板采用8段数码管,结合74HC245芯片的功能和接线方式可以判断,P0管脚通过拉低电平使74HC245芯片的B端输出高电平到共阴极数码管实现点亮相应的灯管。

#include <stdio.h>
#include <reg52.h>
#define SEG P0
void main()
{
	 while(1)
	 {
		SEG = 0x3f;	//让数码管abcdef亮起,g和dp熄灭,对应数字‘0’	
	 }
}

但此时数码管并不能显示,根据数码管的内部结构和原理图连接可知,与已经确定好一端电平的LED灯珠不同的是,LED数码管内的8段灯管的公共端(COM端)的电平并未确定,故不可能有电流流过灯管,故无法发光。

在这里插入图片描述


所以我们需通过控制数码管COM端的电平来选择需要显示的数码管,即位选

在这里插入图片描述

74HC138的功能是:将3位二进制(A0,A1和A2),译码成8种输出状态,并且一共有8个输出I/O,这8位输出的特点是:互斥(同时只有一位有效)、低有效(低电平表示有效,表示选中)。简单来说,74HC138实现了用3根线选择8根线(8选1)的功能。

位选功能通过74HC138芯片实现,由P22、P23、P24控制。
可作仿真图如下:
在这里插入图片描述
由于原理图中74HC245只是用于驱动灯管亮灭,后面串联的电阻用于消除实际PCB中相邻导线的干扰,在仿真中不需要考虑这些问题所以,可以不用画。但上拉电阻不能少。

#include <stdio.h>
#include <reg52.h>
#define SEG P0
sbit A2=P2^4;
sbit A1=P2^3;
sbit A0=P2^2;

void main()
{
	 while(1)
	 {
	 	A2 = 0;
		A1 = 0;
		A0 = 1;
		SEG = 0xcf;		
	 }
}

此处用到keilC51中的关键字sbit定义单片机相应管脚,方便阅读。74HC138芯片输入A2A1A0=001即选中B1,输出高低电平,其余管脚皆为高电平,故第二个数码管亮起

编写段码与‘0’的实现同理,将常见的数字和字母1到9,a到f的段码分别用16进制表示并做成数组,方便取用。

#include <stdio.h>
#include <reg52.h>
#define SEG P0
sbit A2=P2^4;
sbit A1=P2^3;
sbit A0=P2^2;

typedef unsigned int u16;
typedef unsigned char u8;

u8 code SEG_DATA[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,//共阴0 1 2 3 4 5 6 7 8 9
					0x77,0x7c,0x39,0x5e,0x79,0x71};//A B C D E F

void delay(unsigned int ms)
{
	u16 i,j;
	for(i=0;i<ms;++i)
		for(j=0;j<110;++j);
}

void main()
{
	 u16 i=0;
	 while(1)
	 {
	 	A2 = 0;
		A1 = 0;
		A0 = 1;
		for(i=0;i<16;i++)
		{
		   SEG = SEG_DATA[i];
		   delay(200);
		}		
	 }
}

这里定义SEG_DATA数组采用code关键字定义,主要作用是节约单片机RAM空间,且保证程序运行的过程中数据不发生变化

关键字code是51单片机特有关键字,用unsigned int 或signed char等定义的变量都存储在单片机的RAM中,程序中可以随意更改这些变量的值。而运用code关键字修饰下定义的变量,比如unsigned char code i;,它们则存储在单片机程序存储空间FLASH中,节省单片机RAM资源,但在程序中不能更改这些变量的值。
————————————————
版权声明:本文为CSDN博主「鸭鸭_嘎嘎」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_46559794/article/details/108006698


二.数码管动态显示

单个数码管显示的原理搞明白之后,动态显示只需快速切换位选并在同时选择相应显示内容的段码,便可利用人眼的视觉暂留效应,呈现不同的数码管分开显示的效果

#include <stdio.h>
#include <reg52.h>
#define SEG P0
sbit A2=P2^4;
sbit A1=P2^3;
sbit A0=P2^2;

typedef unsigned int u16;
typedef unsigned char u8;

u8 code SEG_DATA[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,//共阴0 1 2 3 4 5 6 7 8 9
					0x77,0x7c,0x39,0x5e,0x79,0x71};//A B C D E F
					
void delay(unsigned int ms)
{
	u16 i,j;
	for(i=0;i<ms;++i)
		for(j=0;j<110;++j);
}
void main()
{
	 u16 i=0;
	 while(1)
	 {
	 	
		for(i=0;i<8;i++)
		{
		   switch(i)
			{
				case(0):
					A2=0;A1=0;A0=0;break;
				case(1):
					A2=0;A1=0;A0=1;break;
				case(2):
					A2=0;A1=1;A0=0;break;
				case(3):
					A2=0;A1=1;A0=1;break;
				case(4):
					A2=1;A1=0;A0=0;break;
				case(5):
					A2=1;A1=0;A0=1;break;
				case(6):
					A2=1;A1=1;A0=0;break;
				case(7):
					A2=1;A1=1;A0=1;break;
			}
		   SEG = SEG_DATA[i];
		   delay(1);
		}		
	 }
}

这里采用switch()函数将二进制位选的数据(000到111)转化成十进制的数字表示(0到7)

在这里插入图片描述

三.玩转数码管

3.1滚动显示

只有八个数码管时怎么显示学号,身份证号等等,大于8位的数据呢?
很简单,只需在动态显示原理的基础之上增加小小的一步——每隔一段时间将位选数据向前挪一位,就可以啦。
既然知道了实现原理,还需考虑代码实现的问题——要在这8个数码管中显示大于8个数据,单片机要怎么才能知道下一位该显示啥?这组数据显示完成了没有?会不会显示到一半又从头开始显示了呢?


这里以循环滚动显示10位数为例——
1.首先将要显示的数据存储在数组中,假设string[10]=[0,1,2,3,4,5,6,7,8,9]
2.接下来定义一个变量i代表着数码管的编号,再定义一个变量j来控制段选数据滚动的位数,j<10。(先只考虑数字的位置变化,不考虑滚动间隔时间)
3.根据这个公式:SEG = string[ ( i + j ) % 10]
i=0,j=0为例,这表示,第一位数码管显示数字‘0’;若j变成‘1’,则第一位数码管显示就是从‘0’变成‘1’。而第八位数码管显示从数字‘7’变成了8;
j继续增大,当 j = 3时,由于i + j % 10=0,则第八位数码管显示string[0]=0,实现循环显示。j继续增大,j=4时,第七位显示0,第八位则显示1,依此类推。
(写得仓促,有令人费解的地方请见谅,有时间的话我会用图表示出来)


下面是代码实现:

#define NUM 10 //显示数据的位数
#define SPACE 2//间隔空间

定义一个数组string用于存放当前数据和缓存数据,其容量为(NUM+SPACE)

void display_smg(void)
{
	u16 i,j,t;   //j控制滚动次数,t实现延时滚动间隙延时
	/*要实现8个数码管滚动显示10个数(其中有2个空位),这里实现的方案是将段选固定,只改变位选内容,即每隔一段时间,就将位选数据往前挪一位*/
	u8 string[(NUM+SPACE)]=
	{0x5b,0x3f,0x5b,0x5b,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x00,0x00};//10位数字加上2个空字符,共12个数据

	for(j=0;j<NUM+SPACE;j++)//j<(NUM+SPACE),这里j<12
	{
		for(t=0;t<80;t++)//调整t的大小可调节滚动延时时间
		{
			for(i=0;i<8;i++)
			{
				switch(i)//将位选数据从段选高位到低位显示/将A2 A1 A0取反即为从低位到高位显示
				{
					case(0):
						A2=1;A1=1;A0=1;break;
					case(1):
						A2=1;A1=1;A0=0;break;
					case(2):
						A2=1;A1=0;A0=1;break;
					case(3):
						A2=1;A1=0;A0=0;break;
					case(4):
						A2=0;A1=1;A0=1;break;
					case(5):
						A2=0;A1=1;A0=0;break;
					case(6):
						A2=0;A1=0;A0=1;break;
					case(7):
						A2=0;A1=0;A0=0;break;
					}
				SEG = string[(i+j)%12];//根据周期取余
				delay(2);
				P0 = 0//消隐
			}
		}
	}
}

完整代码如下:

#include <stdio.h>
#include <reg52.h>
#define SEG P0
#define NUM 10 //显示数据的位数
#define SPACE 2//间隔空间
typedef unsigned int u16;
typedef unsigned char u8;
/*定义数码管位选控制位*/
sbit A2=P2^4;
sbit A1=P2^3;
sbit A0=P2^2;
/*数码管段选数据*/
u8 code SEG_DATA[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,//共阴0 1 2 3 4 5 6 7 8 9
					0x77,0x7c,0x39,0x5e,0x79,0x71};//A B C D E F
/*延时函数*/
void delay(unsigned int ms)
{
	u16 i,j;
	for(i=0;i<ms;++i)
		for(j=0;j<100;++j);
}
void delay100us(void)   //误差 0us
{
    unsigned char a,b;
    for(b=1;b>0;b--)
        for(a=47;a>0;a--);
}
/*数码管滚动显示*/
void display_smg(void)
{
	u16 i,j,t;   //j控制滚动次数,t实现延时滚动间隙延时
	/*要实现8个数码管滚动显示10个数(其中有2个空位),这里实现的方案是将段选固定,只改变位选内容,即每隔一段时间,就将位选数据往前挪一位*/
	u8 string[(NUM+SPACE)]=
	{0x5b,0x3f,0x5b,0x5b,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x00,0x00};//10位数字加上2个空字符,共12个数据

	for(j=0;j<NUM+SPACE;j++)//j<(NUM+SPACE),这里j<12
	{
		for(t=0;t<80;t++)//调整t的大小可调节滚动延时时间
		{
			for(i=0;i<8;i++)
			{
				switch(i)//将位选数据从段选高位到低位显示/将A2 A1 A0取反即为从低位到高位显示
				{
					case(0):
						A2=1;A1=1;A0=1;break;
					case(1):
						A2=1;A1=1;A0=0;break;
					case(2):
						A2=1;A1=0;A0=1;break;
					case(3):
						A2=1;A1=0;A0=0;break;
					case(4):
						A2=0;A1=1;A0=1;break;
					case(5):
						A2=0;A1=1;A0=0;break;
					case(6):
						A2=0;A1=0;A0=1;break;
					case(7):
						A2=0;A1=0;A0=0;break;
					}
				SEG = string[(i+j)%12];//根据周期取余
				delay(2);
				P0 = 0//消隐
			}
		}
	}
}
/*主函数*/		
void main()
{
	 while(1)
	 {
	 		display_smg();
	 }
}

以上代码,利用了三重for循环,除了数组定义不是特别优雅,只需修改宏定义以及数组内容即可显示想要的数据,也算是比较方便。

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值