热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

第3讲数码管显示

一、数码管显示原理我们最经常使用的是七段式和八段式LED数码管,八段比七段多了一个小数点,其它的基本同样。所谓的八段就是指数码管里有八个小LED发光二极管,通过控制不同的LED的亮灭来显

一、 数码管显示原理

我们最经常使用的是七段式和八段式LED数码管,八段比七段多了一个小数点,其它的基本同样。所谓的八段就是指数码管里有八个小LED发光二极管,通过控制不同的LED的亮灭来显示出不同的字形。数码管又分为共阴极和共阳极两种类型,事实上共阴极就是将八个LED的阴极连在一起,让其接地,这样给不论什么一个LED的还有一端高电平,它便能点亮。而共阳极就是将八个LED的阳极连在一起。其原理图例如以下。

1

当中引脚图的两个COM端连在一起,是公共端,共阴数码管要将其接地,共阳数码管将其接正5伏电源。一个八段数码管称为一位,多个数码管并列在一起可构成多位数码管,它们的段选线(即a,b,c,d,e,f,g,dp)连在一起,而各自的公共端称为位选线。显示时,都从段选线送入字符编码,而选中哪个位选线,那个数码管便会被点亮。

数码管的8段,相应一个字节的8位,a相应最低位,dp相应最高位。所以假设想让数码管显示数字0,那么共阴数码管的字符编码为00111111,即0x3f;共阳数码管的字符编码为11000000,即0xc0。能够看出两个编码的各位正好相反。例如以下图。

clip_image004

二、 点亮一个数码管

以下以七段共阴数码管为例讲述如何点亮一个数码管。

l 51系列单片机的P0口没有上拉电阻(其它port有),所以假设直接接数码管的段选线,那么不能将其点亮。我们须要为其加上220欧姆的上拉电阻,注意,上拉电阻阻值不能过大。实验原理图例如以下。

3

当中,7SEG-COM-CAT-GRN为七段共阴数码管,显示为绿色。RES为电阻。查找电阻时,须要选中以下的Resistors,例如以下图。

5

右击选中图中的电阻再左击,弹出的窗体中可改变它的阻值。例如以下图。

clip_image009

那七个电阻看上去非常乱,事实上他们能够用一个排阻(RESPACK-7)取代。例如以下图。

clip_image011

到这里原理图就画完了,我们開始写源程序。让数码管显示字符“0”。

#include

void main()

{

P0 = 0x3f; //P0口送字符‘0’的编码

}

显示效果例如以下。

clip_image013

由于这个程序就一句话,非常easy,所以我们不再进行分析。

三、 一个数码管显示不同字符

以下的程序让一个数码管轮流显示不同的字符。

#include

void delay();

void main()

{

P0 = 0x3f; //显示字符‘0’

delay(); //延时一会

P0 = 0x06; //显示字符‘1’

delay();

P0 = 0x5b; //显示字符‘2’

delay();

}

void delay()

{

int i,j;

for(i=1000;i>0;i--)

for(j=100;j>0;j--);

}

这个程序实现字符‘0’,‘1’,‘2’的循环显示。但假设要循环显示很多其它的数字,每次都写出他们的编码非常麻烦,这里我们能够将全部的编码都写到一个数组里,以后仅仅需调用数组就能够了。程序例如以下。

#include

unsigned char code table[]={0x3f,0x06,0x5b}; //定义编码数组,注意最后的分号

void delay();

void main()

{

P0 = table[0]; //调用数组的第一个元素

delay();

P0 = table[1];

delay();

P0 = table[2];

delay();

}

void delay()

{

int i,j;

for(i=1000;i>0;i--)

for(j=100;j>0;j--);

}

这里要说明的是,unsigned char表明数组中的元素是无符号字符型数据,code表明这是编码数组,其编译后不占内存空间而是占程序存储空间,我们知道单片机的内存空间非常小,所以这个非常重要。table是数组名字,自己能够随便更换。由于数组中的元素是从0開始排的,所以table[0]就是第一个元素0x3f。

四、 多个数码管同一时候显示

原理图例如以下:

8

当中,7SEG-MPX8-CC-BLUE是8位八段共阴数码管,显示为蓝色。其段选线接在P0口,位选线接在P2口。

让全部数码管显示同一个字符。源程序例如以下:

#include

void main()

{

P2 = 0; //P2口各位全为低电平,选中数码管全部位

P0 = 0x3f; //显示字符‘0’

}

这个程序仅仅比第一个程序多了一条“P2 = 0;”,这样来实现位选。终于效果例如以下:

clip_image017

让随意位显示字符。源程序例如以下:

#include

void main()

{

P2 = 0xaa; //选中从左数的第1,3,5,7位数码管

P0 = 0x3f;

}

效果例如以下:

clip_image019

五、 动态显示

以上的显示均为静态显示,以下讲述动态显示。而究竟什么是静态显示什么是动态显示,等看完以下的内容就会非常清楚了。

由于上面多个数码管显示时仅仅能显示同一个字符,怎么才干让不同的数码管显示不同的字符呢?我们先完毕这种一个程序,让第一位数码管显示1,然后第二位数码管显示2,然后第三位数码管显示3。为了使程序短些,我们仅仅控制前三位,要想让其它五位也显示,道理是一样的。

源程序例如以下:

#include

unsigned char code table[]={0x3f,0x06,0x5b,0x4f};

void delay();

void main()

{

P2 = 0xfe; //选中第一位数码管

P0 = table[1]; //让其显示字符‘1’

delay(); //延时一会

P2 = 0xfd; //选中第二位数码管

P0 = table[2]; //让其显示字符‘2’

delay();

P2 = 0xfb; //选中第三位数码管

P0 = table[3]; //让其显示字符‘3’

delay();

}

void delay()

{

int i,j;

for(i=1000;i>0;i--)

for(j=100;j>0;j--);

}

这个程序就是分别选中一位数码管,让它显示一个字符,同单位数码管显示的原理是一样的。这里你会发现每显示完一个字符之后都有一个延时,这个延时有什么作用呢?我们能够先试着改变这个延时,看会有什么效果。我们先将delay()函数中的第一个for循环中的i的初值由1000,改为100,再运行一下程序,有什么效果?然后再将其改为10呢?这时是不是我们想要的不同数码管同一时候显示不同的字符的效果已经出来了。效果例如以下:

clip_image021

这就是上面所说的动态显示效果。那静态显示与动态显示究竟有什么不同呢?非常明显,通俗的说,我们把向数码管各位轮流送入字符编码和位选信号,利用人眼的视觉暂留,让人感觉好像几位数码管被同一时候点亮,这样便能够在不同的数码管上同一时候显示不同的字符的效果称为动态显示。打个比方,你晚上拿根点着的烟,在空中高速划过,你就会看到一条亮线,但事实上它仅仅是一个亮点划过而已。假设你对它还不了解,能够到别的资料上查看一下视觉暂留的相关知识。而静态显示就是真实的同一时候选中几位。这就是它们的根本差别。

六、 消影

到这里我们必须先说明一个问题了。前面我们敲代码都是全部直接写到main()函数里的。那么你有没有想过,main()函数里的语句从头运行到尾,那么语句全部运行完了会怎么样呢?你会想到它会从头再開始运行,对吧!由于由前面的程序能够看出,指令是在无限循环运行的。但依靠这种默认的循环并不可靠,一般地,我们都是在程序中用一个死循环语句来实现无限循环的。上面的源程序的主函数可改为:

void main()

{

while(1) //死循环

{

P2 = 0xfe;

P0 = table[1];

delay();

P2 = 0xfd;

P0 = table[2];

delay();

P2 = 0xfb;

P0 = table[3];

delay();

}

}

能够看到,我们是把全部要循环的语句都放到了一个while(1){}循环中运行的。在以后的程序中,程序的主体部分都会放到这个语句中。

程序写成这样以后,你再将延时函数的延时缩减,比方:

void delay()

{

int i,j;

for(i=5;i>0;i--)

for(j=1;j>0;j--);

}

这时运行程序,是不是发现非常乱了!效果可能例如以下:

clip_image023

这就是我们所说的“拖影”。

事实上在真实的板子上,就算延时非常长,也能够看见“拖影”现象。出现这种现象的原因是CPU的运行速度非常快,当送入位选和段选数据后,接着又送入位选数据,但该位的段选数据还没有送入,所以该位还保持着上次的段选数据,接着该位的段选数据送入,由于视觉残留,两个段选数据的显示效果重合,形成了混乱。简单的说,就是一位数码管显示了它前一位要显示的字符和它本身要显示的字符的重叠效果。要想避免“拖影”就必须在每位数码管显示完后将其关闭,我们能够添“P2 = 0xff;”,这样各位数码管都不会选中,然后下一位再显示时就不会有影响了,这就是所谓的“消影”。我们把程序改为例如以下:

void main()

{

while(1)

{

P2 = 0xfe;

P0 = table[1];

delay();

P2 = 0xff; //消影

P2 = 0xfd;

P0 = table[2];

delay();

P2 = 0xff;

P2 = 0xfb;

P0 = table[3];

delay();

P2 = 0xff;

}

}

可是当运行后,你会发现效果并没有变化。为什么呢?为了研究原因,我们进行联机调试,然后单步运行程序,看看程序究竟是怎么运行的。关于怎么联机调试,我们曾经已经专门讲过,这里不再叙述。

例如以下图,先在keil中按下调试按键clip_image024,会发现Proteus仿真图已经開始运行。然后在keil中选择源程序one显示界面,并按下单步调试clip_image026按键,它表示进入子函数内部,比如以下的调试过程中会进入delay()函数的内部。按下该按键后,会在第一条语句前出现黄色箭头,表明这条语句还没有运行,下一次将会运行该语句。

clip_image028

再次点击单步按键,第一条语句运行完毕,会发现第一位数码管被点亮,由于还没有赋值,所以七段都被点亮了。例如以下图。

clip_image030

再点击单步按键,能够看到尽管段选已经赋值了,但数码管并没有显示。例如以下图。

clip_image032

再点击单步,便进入了delay()函数的内部,此时数码管也显示出‘1’了,例如以下图。

clip_image034

连续点击单步,直到跳出delay()函数,以后我们就点击还有一个单步按键clip_image036,它不会进入子函数内部。例如以下图。

clip_image038

点击单步后,运行完P2=0xff;语句,数码管不再显示,例如以下图。

clip_image040

再点击单步,运行完P2=0xfd;语句,我们发现第二个数码管竟然显示的是‘1’,事实上也对,由于段选的数据还没有改变呢。这正是产生“拖影”的原因。例如以下图。

clip_image042

再点击单步,准备运行延时函数。例如以下图。

clip_image044

点击单步,运行完延时函数后,显示出了正确的字符,例如以下图。

clip_image046

由于已经找到了原因,所以我们联机调试就到这里。能够看到,在进行联机调试单步运行时能够发现非常多程序运行的细节,所以对一些不好想的问题,我们都能够通过这个方案去寻找答案。

我们已经看到程序出错是由于消影语句“P2 = 0xff;”并没有起到应有的作用。那如何才干起到作用呢?刚才在联机调试时我们已经发现仅仅要给了数码管位选数据,它就会被点亮,所以我们能够先给其送入段选数据,然后给其送入位选数据,这样它应该会显示正确字符了,然后延时让它亮一会,再加上消影语句,它就会被熄灭。再给第二位送入段选数据,但这时数码管还是灭这的,它不会产生拖影,此时给其送入位选数据,它就能显示正确的字符了。程序可更改例如以下:

void main()

{

while(1)

{

P0 = table[1];

P2 = 0xfe;

delay();

P2 = 0xff;

P0 = table[2];

P2 = 0xfd;

delay();

P2 = 0xff;

P0 = table[3];

P2 = 0xfb;

delay();

P2 = 0xff;

}

}

这样编译后运行就能完美的显示了。假设你还是不太明确,能够再次进行联机调试,看一下程序的运行过程。

说明:从上面能够看出,就算是两条语句的顺序错误,也会非常大地影响整个程序的运行效果。调试程序是个非常繁杂的工作,为了少出错,我们就要在写源程序时规范我们的语句,从最简单的程序開始,将它研究透了,这样再写大程序时,才不会在这些细节问题上浪费大量的时间。这里我们仅仅是做一个演示样例,还有很多其它的细节知识须要自己在写大量的程序的过程中积累。知识能够传授,但经验是不能传授的。


推荐阅读
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • PBO(PixelBufferObject),将像素数据存储在显存中。优点:1、快速的像素数据传递,它采用了一种叫DMA(DirectM ... [详细]
  • 本题涉及一种由Chip和Dale设计的文本加密方法。该方法通过预先约定的矩阵行数和列数,将字符转换为特定的二进制形式,并以螺旋方式填充矩阵。最终将矩阵中的二进制数连接成一个字符串,实现加密。 ... [详细]
  • 本文整理了一份基础的嵌入式Linux工程师笔试题,涵盖填空题、编程题和简答题,旨在帮助考生更好地准备考试。 ... [详细]
  • 【线段树】  本质是二叉树,每个节点表示一个区间[L,R],设m(R-L+1)2(该处结果向下取整)左孩子区间为[L,m],右孩子区间为[m ... [详细]
  • C语言编写线程池的简单实现方法
    2019独角兽企业重金招聘Python工程师标准好文章,一起分享——有时我们会需要大量线程来处理一些相互独立的任务,为了避免频繁的申请释放线程所带 ... [详细]
  • 题目链接: L - Floating-Point Numbers。题目要求处理带有15位小数的浮点数,计算其二进制表示的最大位数。 ... [详细]
  • 【妙】bug称它为数组越界的妙用
    1、聊一聊首先跟大家推荐一首非常温柔的歌曲,跑步的常听。本文主要把自己对C语言中柔性数组、零数组等等的理解分享给大家,并聊聊如何构建一种统一化的学习思想 ... [详细]
  • 可参照github代码:https:github.comrabbitmqrabbitmq-tutorialsblobmasterjavaEmitLogTopic.ja ... [详细]
  • 在本次学习中,主要通过外部中断来控制LED的亮灭。首先,先查看相关电路图。由图可知,当CC2530端口1的0号引脚输出低电平时࿰ ... [详细]
  • Leetcode学习成长记:天池leetcode基础训练营Task01数组
    前言这是本人第一次参加由Datawhale举办的组队学习活动,这个活动每月一次,之前也一直关注,但未亲身参与过,这次看到活动 ... [详细]
  • Docker 环境下 MySQL 双主同步配置指南
    本文介绍了如何在 Docker 环境中配置 MySQL 的双主同步,包括目录结构的创建、配置文件的编写、容器的创建与设置以及最终的验证步骤。 ... [详细]
  • malloc 是 C 语言中的一个标准库函数,全称为 memory allocation,即动态内存分配。它用于在程序运行时申请一块指定大小的连续内存区域,并返回该区域的起始地址。当无法预先确定内存的具体位置时,可以通过 malloc 动态分配内存。 ... [详细]
  • T15483.【清华集训2017模拟11.26】简单路径T25484.【清华集训2017模拟11.26】快乐树T35485.【清华集训2017模拟11.26】字符串T1结论题,结论很 ... [详细]
  • WinMain 函数详解及示例
    本文详细介绍了 WinMain 函数的参数及其用途,并提供了一个具体的示例代码来解析 WinMain 函数的实现。 ... [详细]
author-avatar
后起之秀
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有