51单片机的自学之路(4)——静态数码管和动态数码管的显示实验
静态数码管的显示实验
数码管的简介
数码管是一种半导体发光器件,其基本单元是发光二极管,也称之为LED数码管。**按照发光二极管的单元连接方式,可以分为共阳极数码管和共阴极数码管。
共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管。共阳数码管在应用时,应该将公众极COM接到+5V,当某一字段发光二极管的阴极为低电平时,相应字段就会被点亮。
对于共阴极数码管而言,在应用的时候应该将公共极COM接到地线GND上。当某一字段的发光二极管的阳极为高电平时,其相应的字段就会被点亮。
这里就举个例子说下如何去点亮,也就是去显示一些数字。
如上图当中,其实可以清楚看到共阴和共阳数码管的结构。对于共阳极而言,当我们要显示数字0的时候,只需要使“a,b,c,d,e,f”这几个二极管的阴极为低电平时,g和dp为高电平,就会显示出这个数字。
同理,对于共阴极数码管而言,如果想要显示数字0,只需要使“a,b,c,d,e,f”这几个二极管的阳极为高电平,g和dp为低电平就可以了。
下面附上共阴和共阳数码管的码表。这对于编程的时候,数码管显示什么内容可以说是至关重要的。
那么问题就来了:上图当中显示0为什么他的码表是0x3f呢?这其实就比较简单了。对于共阴数码管而言,要想显示数字,只需要使得该显示的二极管亮就可以了,也就是输入高电平就可以了,其余的输入低电平。
在之前的时候就提到过,如果要想显示0,必须要给a,b,c,d,e,f这几个二极管输入高电平,也就是说这几位全部都是1,而g和dp为0。那么结合在一起的话,就成了00111111,也就是十六进制数0x3f。
而共阳显示数字的原理和共阴差不多,并且由于输入电平之间的相反,还使得他们输入同一个数字的十六进制数是相反的,比如0x3f和0xC0。
这里需要注意的是:在计算十六进制数的时候,是将a段作为最低位来处理的,b为次低位,以此类推,dp为最高位,不要搞错,切记!
上图为静态数码管的硬件显示图。从图中可以看出,电路是独立的,静态数码管的八个引脚,并没有直接接到单片机的IO口上,而是接到了J8端子。这在连线的时候,就需要单片机的IO口去控制J8端子的输出,并且顺序不能错。至于中间的电阻,作用是限流用的。
下面是静态数码管显示0的程序:
#include<reg51.h> #include<intrins.h> typedef unsigned char u8; u8 code gh[1]={0xC0}; void main() { P0=gh[0]; while(1); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
这个程序就比较简单了。我这边用的是共阳极的静态数码管,市面上比较常用的一种。所以在定义了一个无符号字符型的数组,并且这个数组里面也只有一个元素,那就是0xC0,也就是共阳极数码管0的十六进制数。
这里需要注意的是:因为数组的下标都是从0开始的,所以在给P0口赋值的时候,需要使用P0=gh[0]。这个时候的gh[0]=0xC0。切记这个点!
之后的话,我个人又做一个类似的实验,就是用共阳极的静态数码管去循环的显示0-F这16个数。下面分享下实验程序:
#include<reg51.h>
#include<intrins.h>
typedef unsigned char u8;
u8 code gh[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; // 这是共阴极数码管0-F的值,下面用的时候取反就ok
void delay1s(void) //误差 1s
{
unsigned char a,b,c;
for(c=167;c>0;c--)
for(b=171;b>0;b--)
for(a=16;a>0;a--);
_nop_(); //if Keil,require use intrins.h
}
void main()
{
unsigned char i;
for(i=0;i<17;i++)
{
P0=~gh[i];
delay1s();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
因为是要循环显示嘛,所以肯定是需要延时程序的,我这里用软件生成了1s的延时程序。后面的主程序当中,用了一个简单的for循环,作用就是循环的输出0-F这16个数。
动态数码管显示实验
动态数码管和静态数码管的直接区别就是:他们的公共端的状态,前者是独立的,后者是连接在一起的。
当多位一体时,它们内部的公共端是独立的,而负责显示什么数字的段线 全部是连接在一起的,独立的公共端可以控制多位一体中的哪一位数码管点亮, 而连接在一起的段线可以控制这个能点亮数码管亮什么数字,通常我们把公共端 叫做“位选线”,连接在一起的段线叫做“段选线”,有了这两个线后,通过单 片机及外部驱动电路就可以控制任意的数码管显示任意的数字了。
要想做好动态数码管的实验,就一定要熟悉和了解“位选线”和“段选线”的内容和区别,知道他们各自的作用是什么。前者是管理着那一个数码管亮,后者去管理这个亮的数码管显示什么内容,这样理解就很简单易懂。
数码管静态显示,也就是显示内容一样的时候,这是因为他们的“段选线”是连接在一起的。而动态显示,他们的“段选线”是分开的,利用位选线不同时选择通 断,改变段选数据来实现的。
并且必须要知道的是:人的肉眼正常情况下只能分辨变化超过 24ms 间隔的运动。 也就是说,在下一次点亮 0 这个数字的时间差不得大于 24ms。这也就要求了延时时间不得大于24ms。
而想要做好实验,还必须认识两种芯片,一个是 74HC245,其作用主要就是用于大屏显示,也就是段选线一般连接的地方。
而74HC138 芯片,这是一个可以通过少量的单片机IO口,就可以产生很多输出口的芯片,是一种三通道输入、八通道输出译码器。
对于前者可以不需要记太多。但是对于38译码器,我们必须要有足够的认识,因为在日常的生活当中,这种译码器是经常被运用的,毕竟占用单片机的IO口资源少,方便。
上图给出了38译码器的真值表(这个可以去记,但可以看我下面的小诀窍再记,毕竟看着都乱)。在真值表当中,一般是通过E1,E2和E3这三个使能信号和A0,A1,A2输入信号,去控制8个输出口那一个输出低电平,使得相应的数码管亮起来的。
而记这个真值表的诀窍如下:**A0、A1、A2 输入就相当于 3 位 2 进制数,A0 是 低位,A1 是次高位,A2 是高位。而 Y0-Y7 具体哪一个输出有效电平,就看输入 二进制对应的十进制数值。比如输入是 101(A2,A1,A0),其对应的十进制数 是 5,所以 Y5 输出有效电平(低电平)。
上图是动态数码管模块,从这个模块当中,我们可以清楚的看到:J1端子控制着八个共阴数码管,也就是我们之前提到的位选线。而J6端子,接上几个电阻,控制的是之前提到的段选线,也就是控制显示的内容。
再来看下非常重要的38译码器。输入端为J9控制的1,2和3,输出端是J10端子的8个端口。因为在我做的实验当中,把J10和J1进行了短接,所以这就使得J9端子成为了控制8个数码管谁亮的“位选线”。所以这里就用到了我们之前说的真值表,也就是A0,A1和A2。
连接好的硬件之后,接下来就是写实验程序了:
#include<reg51.h> #include<intrins.h> sbit gh1=P1^0; sbit gh2=P1^1; sbit gh3=P1^2; typedef unsigned char u8; u8 code gh[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; // 这是共阴极数码管0-F的值,下面用的时候取反就ok void delay20us(void) //误差 20us { unsigned char a,b; for(b=1;b>0;b--) for(a=7;a>0;a--); } void DigDisplay() { unsigned char i; for(i=0;i<8;i++) { switch(i) {case(0): gh1=0;gh2=0;gh3=0;break;//跳出循环 case(1): gh1=1;gh2=0;gh3=0;break; case(2): gh1=0;gh2=1;gh3=0;break; case(3): gh1=1;gh2=1;gh3=0;break; case(4): gh1=0;gh2=0;gh3=1;break; case(5): gh1=1;gh2=0;gh3=1;break; case(6): gh1=0;gh2=1;gh3=1;break; case(7): gh1=1;gh2=1;gh3=1;break; } P0=gh[i]; delay20us(); P0=0x00; } } void main() { DigDisplay(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
这个程序呢,其实也很好理解。和之前的静态数码管程序不同的是,就是中间多了switch case语句,而他的作用,主要就是为了选择“位选”,也就是选择那一个数码管亮。
而在这个实验当中,因为我个人把延时时间设置到了20ms,这使得显示的0-7个数字在一直不停的循环点亮跳动,若隐若现的感觉,有点意思。
而在这个实验做完之后,我另外做了两个实验。一个是在八个数码管上显示1314-520。
实验程序如下:
#include<reg51.h>
typedef unsigned char u8;
u8 code gh3[17]={0x06,0x4f,0x06,0x66,0x40,0x6d,0x5b,0x3f, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};// 显示共阴数码管0-F的值。
sbit gh=P1^0;
sbit gh1=P1^1;
sbit gh2=P1^2; //对三个位选进行定义位变量,之后用变量名替代三个IO口。
void delay10us() //误差 0us
{
unsigned char a,b;
for(b=1;b>0;b--)
for(a=2;a>0;a--);
}
void DigDisplay() //动态扫描函数,目的就是循环扫描八个数码管的显示。
{
unsigned char i;
for(i=0;i<8;i++)
{
switch(i)
{
case(0):
gh=0;gh1=0;gh2=0;break;// 显示第0位,同时跳出循环。
case(1):
gh=1;gh1=0;gh2=0;break;// 显示第1位。
case(2):
gh=0;gh1=1;gh2=0;break;
case(3):
gh=1;gh1=1;gh2=0;break;
case(4):
gh=0;gh1=0;gh2=1;break;
case(5):
gh=1;gh1=0;gh2=1;break;
case(6):
gh=0;gh1=1;gh2=1;break;
case(7):
gh=1;gh1=1;gh2=1;break;
}
P2=gh3[i];
delay10us();
P2=0x00;
}//数组下标是从0开始的啊!
}
void main()
{
DigDisplay();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
这个其实上第一个没啥太大区别,无非就是段选的内容进行了变化。这里需要注意的是:这几个数字之间,“-”所使用的十六进制数,也就是0x40的由来。
在静态数码管当中,想要显示“-”,那肯定是需要让某一个led的阳极接高电平的(因为这里用的是八个共阴数码管)。而在看了数码管的结构之后,自然就可以得出g=1的结论,也就是0100 0000,这里的高地位要分清楚,dp和g在高位和次高位。
而另外一个实验就是:在不适用38译码器的情况下,让单片机的IO口直接控制位选线,也就是把共阴数码的八个位选线的数值,赋值给单片机的IO口。这里需要注意的是“共阴数码管,位选为低电平(0)时,才会选中数码管;共阳数码管,位选为高电平(1)时,才会选中数码管”。
在这种情况下,两种数码管的位选值分别如下:
共阴:
unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮,即位码
共阳:
unsigned char code distab[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//位选
程序如下:
#include<reg51.h> #include<intrins.h> sbit gh1=P1^0; sbit gh2=P1^1; sbit gh3=P1^2; typedef unsigned char u8; typedef unsigned char u9; u8 code gh4[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//共阴数码管的位选值,在处于低电平0的情况下 u8 code gh[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; // 这是共阴极数码管0-F的值,下面用的时候取反就ok void delay20us(void) //误差 20us { unsigned char a,b; for(b=1;b>0;b--) for(a=7;a>0;a--); } void DigDisplay() { unsigned char i; for(i=0;i<8;i++) { P1=gh4[i]; P0=gh[i]; delay20us(); P0=0x00; } } void main() { DigDisplay(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
OK。写了很久,实验也做了很久。作为一个新手,一个小白,能尽量搞懂的就尽量搞懂,不能到了后面开始出现前面没学好,不熟悉的情况出现。就这吧,继续加油!晚上还要做一个实验!