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。写了很久,实验也做了很久。作为一个新手,一个小白,能尽量搞懂的就尽量搞懂,不能到了后面开始出现前面没学好,不熟悉的情况出现。就这吧,继续加油!晚上还要做一个实验!

(0)

相关推荐

  • 【学习笔记】单片机的40个经典实验之10: 00-99 计数器

    一.实验任务 利用 AT89S51 单片机来制作一个手动计数器,在 AT89S51 单片机的 P3.7 管脚接一个轻触开关,作为手动计数的按钮,用单片机的 P2.0-P2.7 接一个共阴数码管,作为 ...

  • 自学单片机第一天:51单片机的构成,软件环境搭建

    https://m.toutiao.com/is/JcoW9Gd/ 学了一段C语言基础,买了ARM(4412)开发板准备学嵌入式,没有任何基础,看了几天裸机教程,头大啊真是从入门到放弃.欲速则不达,还 ...

  • 0 基础 Java 自学之路(2021年最新版)

    如果你想自学 Java,认真看完本文,你以后的职场生涯至少少走1年弯路. 本文会持续更新,建议收藏. 初衷 在 CSDN 上经常有同学私聊我询问 "如何自学 Java"." ...

  • 增强型51和传统51单片机外设操作的区别

    文/Edward 前面的内容,我们从传统的51单片机出发,从硬件的基础上,一步步衍生出了增强型51单片机所增强的地方.现在我们可以清楚地了解,增强型51单片机对比传统的51单片机,增强的地方在于存储器 ...

  • 增强型51单片机扩展方式

    文/Edward 大家是否思考过一个问题,为什么现在的51单片机在做宣传的时候,不外乎都是称作为"增强型51单片机"?与传统的51单片机相比,究竟哪些地方得到了扩展? 在回答这个问 ...

  • 一天入门51单片机

    本套教程共3节课程,熟悉这3节课程的话,你已经入门51单片机了. 下面是内容正文 单片机学习的第一步,什么是单片机最小系统? 我来打个比喻吧. 我们都知道,人的大脑是可以控制眼耳口鼻,手脚,全身等等, ...

  • 51单片机的存储空间梳理

    文/Edward我们在学习传统的MCS-51单片机的时候,一定学习过51单片机的存储结构.传统的MCS-51存储器有三个空间,分别是片内RAM(内部数据存储器).片外RAM(外部扩展的数据存储器) 和 ...

  • 单片机“朝花夕拾”系列——乐创增强型51单片机讲义序

    本来想的是认认真真地做一部ARM内核的MCU教程和文档,这个入口最好的芯片切入口就是STM32.然而最不凑巧的是,现在STM32无论哪一个型号都是一"芯"难求.以至于我Layout ...

  • 如何看待增强型51单片机?

    文 / Edward  1  辩证地看待51内核单片机 要回答这个问题,首先需要来看一下传统的51单片机.在有些文章或者书本中,对于51单片机的态度是全面否定的,一旦听到某人要学习或者使用51单片机, ...

  • 51单片机 4个独立按键控制LED灯 (protues仿真)(C语言版)

    一.思路及相关问题 1.逻辑思路: 当按下key1时 led1亮起来 因为按下去具有瞬时性不用while,用if语句,当松开时,用while(!key1) ,函数调用. 先在脑海里想象它的大致运行过程 ...