目录
1.数码管原理
一位数码管引脚定义:
四位一体数码管:
多个数码管同时显示不同数字
51单片机的数码管的原理图
51单片机实现静态显示和动态显示
静态显示:
动态显示:
1.数码管原理
一位数码管引脚定义:
数码管是由8个LED组成的,其中共阴极数码管是8个LED共用一个阴极;共阳极数码管是8个LED共用一个阳极;每个数码管灯由图中左下角的8段LED构成,分别是ABCDEFG以及DP;正好和1个字节的8位相对应;从图中也可以看出,8段LED中,每段LED对应的引脚图,即每段LED和引脚之间的连接关系,基本是按照就近原则来连接的,比如A段,A段的正极连接3和8引脚(在共阳极数码管的情况下),A段的负极连接7号引脚;以此类推,其他引脚关系都可从图中看出来。
值得注意的是,我们开发板上的连接方式是共阴极连接。
如果想要使这个数码管显示数字“6”,该怎么操作呢?需要让A、F、E、D、C、G都亮起来即可。那么如何使A、F、E、D、C、G都点亮,其他的熄灭呢?参照上面的图可知,对于共阴极连接方式,首先3和8引脚都是要接地的(即负极、低电平、0);然后A、F、E、D、C、G都接正极(高电平,1),B和DP接负极,这样就能得到一个数字“6”,即A、B、C、D、E、F、G、DP设置为1011 1110;
下面为proteus仿真视频
proteus单个数码管仿真
实现9到0依次循环,代码如下 :
#include <REGX52.H>
void Delay(unsigned int xms)
{unsigned char i, j;while(xms--){i = 2;j = 239;do{while (--j);} while (--i);}
}
void main()
{while(1){P2=0x6F;Delay(500);P2=0x7F;Delay(500);P2=0x07;Delay(500);P2=0x7D;Delay(500);P2=0x6D;Delay(500);P2=0x66;Delay(500);P2=0x4F;Delay(500);P2=0x5B;Delay(500);P2=0x06;Delay(500);P2=0x3F;Delay(500);}
}
因为我是P2-7接的是G,P2-6接的是F..........反过来的,所以根据上面的引脚图要反着来计算二进制的值。
四位一体数码管:
可以看到,以共阴极数码管为例,每个数码管共用阴极,但是四个数码管阳极所有的对应引脚都是连接同一个引脚;例如这四个数码管的A段LED都是连接在一起的,和11引脚相连;这四个数码管的B段LED都是连接在一起的,和7引脚相连。这样做的好处是节省单片机的引脚,如图中有4*8=32个LED段,但是只用12个引脚就可以了。
对于这种数码管,如果想让第三位数码管显示一个数字1,怎么操作?
因为是共阴极,所以12、9、8、6正常来讲都是负极才能点量对应的数码管。要想达到只第三个数码管显示1的目的:其中引脚12、9、6这三个阴极都置为1(高电平),这样第一、二、四个数码管就都不会亮;将引脚8设置为0(低电平),这样第三个数码管就有了电路连通的条件;然后再将B断和C段对应的引脚置为高电平,即7和4引脚置为1(高电平),此时就能第三个数码管中的B和C段对应的LED灯就亮了,其他段的灯不亮,即达到了只第三个数码管显示1的目的。
proteus仿真如下:
代码是P2=0x60;我这里P2.7接的是A,根据这两个例子,P2的值赋值是不是要看数码管的A,B,C,D.....与P2的8个引脚怎么接才行,这里P2.7接A,依次下去,亮一,就赋值P2=0x60。
比如P2.0接A,亮1就赋值0x06,所以一定要看自己的单片机原理图是如何接的,还要看是共阴极还是共阳极,
下面我就给出两种情况显示各个数字的值(小数点dp都不点亮)(共阴极数码管)
第一种:P2.0接A,依次接下去,(例如视频所示)
第二种:P2.7接A,依次接下去:(例如上面图所示)
0 : 0xFE 1: 0x60 2: 0xDA 3: 0xF2 4: 0x66 5: 0xB6
6 : 0xBE 7 : 0xD0 8: 0xFE 9:
多个数码管同时显示不同数字
关于上面讲到的,这四个数码管四个数码管阳极所有的对应引脚都是连接同一个引脚,这样导致的结果是,如果我们想让第二个数码管和第三个数码管同时点亮,且第二个和是第三个数码管显示不同的数字,是无法做到的,因为即使为了让第二个数码管亮而将9号引脚置为0,那么此时9和8引脚都为0,7和4引脚都为1,结果是第二个二极管和第三个二极管都显示数字1,无法达到显示不同数字的目的;所以因为四个二极管对应段LED都共用同一个引脚,导致的结果是只能显示同样的数字。
那么如何使不同的数码管显示不同的数字呢?这就是我们的目标2要实现的了,即动态数码管显示;利用的原理是人眼的视觉暂留和数码管显示的余晖原理;比如我们想达到让前三个数码管分别显示“1”“2”“3”的目的,经过上面的理论我们可以知道让三个数码管同时显示三个不同的数字是不可能的,但是我们可以让第一个数码管显示数字1,然后迅速的让第二个数码管显示数字2,然后迅速的让第三个数码管显示数字3,这要他们的间隔足够短,在我们视觉上看起来就像是同时在显示1、2、3一样。
#include <REGX52.H>//数码管段码表
unsigned char NixieTable[]={0xFC,0x60,0xDA,0xF2,0x66,0xB6,0xBE,0xD0,0xFE,0xF6};//延时子函数
void Delay(unsigned int xms)
{unsigned char i, j;while(xms--){i = 2;j = 239;do{while (--j);} while (--i);}
}//数码管显示子函数
void Nixie(unsigned char Location,Number)
{switch(Location) //位码输出{case 1:P0_0=1;P0_1=1;P0_2=1;P0_3=1;break;//4个数码管都不选case 2:P0_0=1;P0_1=1;P0_2=1;P0_3=0;break;//选择第四个数码管case 3:P0_0=1;P0_1=1;P0_2=0;P0_3=1;break;//选择第三个数码管case 4:P0_0=1;P0_1=1;P0_2=0;P0_3=0;break;//选择第3和第4个数码管case 5:P0_0=1;P0_1=0;P0_2=1;P0_3=1;break;//选择第二个数码管case 6:P0_0=1;P0_1=0;P0_2=1;P0_3=0;break;//选择第2和第4个数码管case 7:P0_0=1;P0_1=0;P0_2=0;P0_3=1;break;//选择第2和第3个数码管case 8:P0_0=1;P0_1=0;P0_2=0;P0_3=0;break;//选择第2和第3和第4个数码管case 9:P0_0=0;P0_1=1;P0_2=1;P0_3=1;break;//选择第一个数码管case 10:P0_0=0;P0_1=1;P0_2=1;P0_3=0;break;//选择第1和第4个数码管case 11:P0_0=0;P0_1=1;P0_2=0;P0_3=1;break;//选择第1和第3个数码管case 12:P0_0=0;P0_1=1;P0_2=0;P0_3=0;break;//选择第1和第3和第4个数码管case 13:P0_0=0;P0_1=0;P0_2=1;P0_3=1;break;//选择 1 2 号数码管case 14:P0_0=0;P0_1=0;P0_2=1;P0_3=0;break;//选择1 2 4 号数码管case 15:P0_0=0;P0_1=0;P0_2=0;P0_3=1;break;//选择1 2 3 号数码管case 16:P0_0=0;P0_1=0;P0_2=0;P0_3=0;break;//四个数码管全选}P2=NixieTable[Number]; //段码输出Delay(1); //显示一段时间P2=0x00; //段码清0,消影
}void main()
{while(1){Nixie(9,1); //在数码管的第1位置显示1
// Delay(20);Nixie(5,2); //在数码管的第2位置显示2
// Delay(20);Nixie(3,3); //在数码管的第3位置显示3
// Delay(20);Nixie(2,0); }
}
注意: 虽然我switch case 把所有的情况都列出来了,也就是位选的所有情况,但是,动态扫描时,一个数码管是显示一位数字,所以尽量要选择单个数码管,代码中的case9 5 3 2。如果你选中的是多个数码管,那么他只会显示相同的数字,而且,你选中多个数码管时,main语句时要注意不要重复选中相同的数码管,比如不能同时选择case7 8,否则会出错,下图所示是选择4个数码管一起亮,显示5:
代码是:Nixie(16,5);
上面是用了C语言的数组,和switch case语句,调用函数,我们讲一下数组就OK了,switch case 还是很好理解的
有了这些基础,我们看看51单片机的数码管的相关原理图
51单片机的数码管的原理图
显然,8个数码管的位选(选择哪一个数码管亮)由单片机3个引脚控制,这样的好处大大缩小了引脚的数量,不用每一个LED对应一个引脚。下面我们讲一讲38译码器电路。
38译码器是三个线到八个线的译码器;其实就是负责将三个输入A、B、C(P22-P24)转成八个输出Y0-Y7(也对应LED1-8);A、B、C三个输入值分别表示为三个二进制的数(C为高位,B为中位,A为低位,即数字组成为CBA),这三个输入值,转换成十进制,就对应他们的输出。
例如输入A为0,B为0,C为0,则输入为000,其对应的十进制值是0,即Y0有效,其他无效(低电平为有效,高电平位无效),所以Y0是0,Y1-Y7为1;
再例如,如果输入A为1,B为0,C为0,则输入为001(CBA,前面说了,C为高位,B为中位,A为低位),其对应的十进制为1,即Y1有效,其他无效(低电平为有效,高电平位无效),所以Y1是0,Y0和Y2-Y7为1;刚好对应我们的8个数码管,
以此类推。。。。
其真值表如下所示
有了这些知识,比如我们要选中第三个数码管亮,LED(1~8)的值是
对应38译码器是不是 101 P2_4=1,P2_3=0,P2_2=1,即可
下面我们再来看数74HC245(双向据缓冲器)
根据原理图我们是不是知道,数码管的段选都与这个芯片连接,即晶体管的正极是接在74HC245(双向数据缓冲器)上的。为什么不直接接在CPU上呢?还要多此一举呢?
因为单片机的高电平驱动能力太弱,这个缓存器提高驱动能力。这样单片机的I/O口就变成了控制信号,不需要很高的电流驱动,把数据给了这个芯片之后,这个芯片有VCC给他供电,就很好的驱动数码管,亮度得到了保证。
DIR的高低电平决定了数据的流向;在74HC245中,如果将DIR,也就是LE(1引脚)设置为高电平,则表示数据流向为从左边到右边。由下图可知,A0对应B0,A1对应B1,以此类推;而B0-B7对应LCD0-7;
总结一下说就是,输入给P00-P07什么数据,该数据就原封不动的送给LCD0-LCD7
。
电容的作用:给VCC滤波,稳定电源,确保电路稳定性。提高电路工作性能。
电阻:限流
74HC245作用:双向的数据缓冲
51单片机实现静态显示和动态显示
静态显示:
第二个数码管显示5
第一步是选择使哪个晶体管点亮:即要让目标点亮的等负极有效,而负极是接在138译码器上的,138译码器的8输出连接着静态数码管的负极,138译码器的3个输入连接着MCU的引脚(P22-P24口);所以我们要通过对P22-P24口的设置,来控制目标晶体管负极有效。
第二步是设置该目标点亮的晶体管具体显示什么数字;即通过对正极引脚的设置,控制具体LED的8段LED哪几段点亮,用以生成目标数字。晶体管的正极是由74HC245的P00-P07等8个引脚来输入的;
总结:
- 第一步,选择哪个晶体管点亮,控制引脚为P22-P24;
- 第二步,选择具体点亮的数字,控制引脚为P00-P07;
#include <REGX52.H>void main()
{while(1){P2_2=0;P2_3=1;P2_4=0;P0_0=1;P0_1=0;P0_2=1;P0_3=1;P0_4=1;P0_5=1;P0_6=1;P0_7=0;}
}
最后,我们可以对代码进行完善,写一个函数,两个入参分别为:第一个参数为第几个等亮,第二个参数为显示什么数字,这样就能通过调用该函数时,传入参数的不同,控制不同的灯显示不同的数字,代码如下:
#include <REGX52.H>//数码管段码表
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};//数码管显示子函数
//Location表示第几个数码管点亮;Number表示该数码管显示什么数字
void Nixie(unsigned char Location,Number)
{switch(Location) //位码输出{case 1:P2_4=1;P2_3=1;P2_2=1;break;case 2:P2_4=1;P2_3=1;P2_2=0;break;case 3:P2_4=1;P2_3=0;P2_2=1;break;case 4:P2_4=1;P2_3=0;P2_2=0;break;case 5:P2_4=0;P2_3=1;P2_2=1;break;case 6:P2_4=0;P2_3=1;P2_2=0;break;case 7:P2_4=0;P2_3=0;P2_2=1;break;case 8:P2_4=0;P2_3=0;P2_2=0;break;}P0=NixieTable[Number]; //段码输出
}void main()
{Nixie(2,5); //在数码管的第3位置显示数字6while(1){}
}
动态显示:
实现动态数码管的方法和代码,即如果想让第一个数码管显示1,第二个数码管显示2,第三个数码管显示3,且让他们同时显示;方法就是先让第一个显示1,然后立马让第二个显示2,再立马让第三个显示3;只要他们之间的间隔足够短,在我们视觉效果上看来就是在同时显示。
代码如下
#include <REGX52.H>//数码管段码表
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};//延时子函数
void Delay(unsigned int xms)
{unsigned char i, j;while(xms--){i = 2;j = 239;do{while (--j);} while (--i);}
}//数码管显示子函数
void Nixie(unsigned char Location,Number)
{switch(Location) //位码输出{case 1:P2_4=1;P2_3=1;P2_2=1;break;case 2:P2_4=1;P2_3=1;P2_2=0;break;case 3:P2_4=1;P2_3=0;P2_2=1;break;case 4:P2_4=1;P2_3=0;P2_2=0;break;case 5:P2_4=0;P2_3=1;P2_2=1;break;case 6:P2_4=0;P2_3=1;P2_2=0;break;case 7:P2_4=0;P2_3=0;P2_2=1;break;case 8:P2_4=0;P2_3=0;P2_2=0;break;}P0=NixieTable[Number]; //段码输出Delay(1); //显示一段时间P0=0x00; //段码清0,消影
}void main()
{while(1){Nixie(1,1); //在数码管的第1位置显示1
// Delay(20);Nixie(2,2); //在数码管的第2位置显示2
// Delay(20);Nixie(3,3); //在数码管的第3位置显示3
// Delay(20);}
}
其中的P0=0x00;
表示每次循环结束时都将所有的数码管进行清零,即全部熄灭,免得留下灯的残影。