资讯详情

51单片机外设篇:数码管

数码管简介

LED数字管:数字管是一种简单、廉价的显示器,是一种由多个发光二极管组成的8字装置。例如,红绿灯。

单个数码管:

多数码管:

这些引脚由相应的寄存器控制,具体查看原理图和数据手册。

编程时要注意是共阴极还是共阳极。

注意,共阳极数字管一般是共阳极外部电源电路,有足够的电流照亮数字管,如果是共阴极数字管,则需要单片机提供阳极电流,一般单片机电流不大,所以通常连接芯片,发挥放大电流的作用,如下74573。

选择信号决定亮哪个数字管,段选信号决定显示数字。

因为数字引脚是共用的,一次只能显示一个数字。即使选择多个数字管,多个数字管也只能显示相同的数字,称为数字管的静态显示。

那么如何动态显示呢?有必要使用视觉暂留效应。让不同的数字管依次显示不同的数字。只要单片机扫描的频率到人眼无法察觉,就可以在不同的数字管中显示不同的数字。

比如:

while(1){

数字管1显示1;

数字管2显示2;

数字管3显示3;

}

由于频率很快,人们无法察觉到它在闪烁。然而,可以通过在中间添加延迟来验证。

比如:

while(1){

数字管1显示1;

delay(1);

数字管2显示2;

delay(1);

数字管3显示3;

delay(1);

}

虽然这种情况可以动态显示,但会有消隐现象。我们来看看这个过程。

位选 段选

位选 段选

位选 段选

每一轮重复扫描,上一轮LED段落还没有完全熄灭,下一轮就来了,上一轮就会有残影。解决方案是在每一轮显示后清除段选(注意是段选,不是位选,通过138译码器连接不能关闭)。

while(1){

数字管1显示1;

上一段选清零;

数字管2显示2;

上一段选清零;

数字管3显示3;

上一段选清零;

}

单片机直接扫描:硬件设备简单,但会消耗大量单片机CPU时间;

所以现在有专用的驱动芯片。

专用驱动芯片:内部自带显存、扫描电路,单片机只需告诉它显示什么即可。

静态数字管原理图

本质上,数码管是8LED由灯组成。操作模式和LED灯光没有本质区别。

将JP3和P0端口用杜邦线连接。

由于引脚对应的顺序不清楚,可能需要测试下端口的对应关系。

显示数字6,代码如下:

/**   *@file pipeshowsix.c   *@author Timi   *@date 2022.07.13   *@version 1.0   */ #include <reg51.h>  #define NUMSIX 0x82  void ShowSix();  void main(void) {        ShowSix(); }  /**   *@brief 静态数码管显示6   *@param[in]   *@param[out]    *@return   */ void ShowSix() {         P0 = NUMSIX; }

动态数码管

通过位置选择,可以共享8个数码管的段选端。

原理图如下:

可见需要两条杜邦线。

我将J12连接到P0端口,将J16连接到P1端口。

由于段选端是共用的,如果想要8个数字管显示相同的数字,则相对简单。

但要同时显示8个数字管的不同数字,需要扫描。

例如,如果8个数字管从前到后显示1234578,则首先显示1,然后显示2,然后显示3……第八个数字管显示8,因为代码运行太快,人眼跟不上变化,所以看起来是同时显示的。实现代码如下:

下面简单介绍一下74。LS138芯片的基本情况及使用注意事项:

74LS138 为3 线-8 共有线译码器 54/74S138和 54/74LS138 74LS138的工作原理如下:当选择端时(G1)是另外两个选通端(/(G2A)和/(G2B))地址端可用于低电平时(A、B、C)二进制编码在相应的输出端低电平翻译。

下图为其原理结构图和真值表:

我们可以从逻辑图和功能表中看到744LS138的8个输出管脚要么在任何时候都是高电平1-芯片不工作,要么只有一个是低电平0,其余7个输出管脚都是高电平1。如果两个输出管脚在同一时间为0,损坏。

那么,通过连接译码器来减少引脚的使用吗?将原本需要的8个引脚减少到3个引脚。我的理解是这样的。

如果是这样的话,我需要J6连接到P1端口的P1.0、P1.1、P1.2。

代码如下:

/**   *@file    scantube.c   *@author  Timi   *@date    2022.07.14   */ #include <reg51.h>  ///数字管数字编码 #define TUBE_NUM0 0x3F #define TUBE_NUM1 0x06 #define TUBE_NUM2 0x5B #define TUBE_NUM3 0x4F #define TUBE_NUM4 0x66 #define TUBE_NUM5 0x6D #define TUBE_NUM6 0x7D #define TUBE_NUM7 0x07 #define TUBE_NUM8 0x7F #define TUBE_NUM9 0x6F #define TUBE_NOSHOW 0x0;  ///数码管位选择 #define TUBE1 0x0 #define TUBE2 0x1 #define TUBE3 0x2 #define TUBE4 0x3 #define TUBE5 0x4 #define TUBE6 0x5 #define TUBE7 0x6 #define TUBE8 0x7  void FirstTubeSixToZero(); void TubeShowOneToEight(); void Delay();          ///函数入口 void main(void) {        FirstTubeSixToZero();     Delay();     TubeShowOneToEight(); }  /**   *@brief   *@param[in] LED的编号,0—7   *@param[out]    *@return   */ void FirstTubeSixToZero() {     P1 = TUBE1;     P0 = TUBE_NUM6;     Delay();     P0 = TUBE_NUM5;     Delay();     P0 = TUBE_NUM4;     Delay();     P0 = TUBE_NUM3;     Delay();     P0 = TUBE_NUM2;     Delay();     P0 = TUBE_NUM1;     Delay();     P0 = TUBE_NUM0; }  /**   *@brief   *@param[in]   *@param[out]    *@retrn
  */
void TubeShowOneToEight()
{
    while(1)
    {
        P1 = TUBE1;
        P0 = TUBE_NUM1;
        P0 = TUBE_NOSHOW;
       
        P1 = TUBE2;
        P0 = TUBE_NUM2;
        P0 = TUBE_NOSHOW;
        
        P1 = TUBE3;
        P0 = TUBE_NUM3;
        P0 = TUBE_NOSHOW;
        
        P1 = TUBE4;
        P0 = TUBE_NUM4;
        P0 = TUBE_NOSHOW;
        
        P1 = TUBE5;
        P0 = TUBE_NUM5;
        P0 = TUBE_NOSHOW;
        
        P1 = TUBE6;
        P0 = TUBE_NUM6;
        P0 = TUBE_NOSHOW;
        
        P1 = TUBE7;
        P0 = TUBE_NUM7;
        P0 = TUBE_NOSHOW;
        
        P1 = TUBE8;
        P0 = TUBE_NUM8;
        P0 = TUBE_NOSHOW;
    }
}

/**
  *@brief 延时
  *@param[in]
  *@param[out]
  *@return
  */
void Delay()
{
    int i = 0, j = 0;
    for(i; i < 30000; i++)
    {
        for(j; j < 30000; j++);
    }
}

优化总结

其实不难从上面的程序看出来,有太多的重复代码了。

这时候,就需要重构,可以考虑使用函数再次进行封装;或者将相似的参数组装成一个数组,然后用循环语句来解决。

如下所示:

/**
  *@file    scantube.c
  *@author  Timi
  *@date    2022.07.14
  */
#include <reg51.h>

void FirstTubeEightToZero(unsigned char tube[], unsigned char tubeNum[], int tubeNumCount);
void TubeShowOneToEight(unsigned char tube[], unsigned char tubeNum[], int tubeCount, int tubeNumCount);
void Delay();
        
//函数入口
void main(void)
{   
    //数码管位选
    unsigned char tube[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
    //数码管数字编号0—9,最后一位为全不亮
    unsigned char tubeNum[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x00};
    int tubeCount = sizeof(tube) / sizeof(tube[0]);
    int tubeNumCount = sizeof(tubeNum) / sizeof(tubeNum[0]);
    
    FirstTubeEightToZero(tube, tubeNum, tubeNumCount);
    TubeShowOneToEight(tube, tubeNum, tubeCount, tubeNumCount);
}

/**
  *@brief   8—0数倒计时
  *@param[in]
  *@param[out] 
  *@return
  */
void FirstTubeEightToZero(unsigned char tube[], unsigned char tubeNum[], int tubeNumCount)
{
    int i = tubeNumCount - 3;
    P1 = tube[0];
    
    for(i; i >= 0; i--)
    {
        P0 = tubeNum[i]; 
        Delay();
    }
}

/**
  *@brief
  *@param[in]
  *@param[out] 
  *@return
  */
void TubeShowOneToEight(unsigned char tube[], unsigned char tubeNum[], int tubeCount, int tubeNumCount)
{    
    while(1)
    {     
        int i = 0;
        
        for(i; i < tubeCount; i++)
        {
            P1 = tube[i];
            P0 = tubeNum[i + 1];
            P0 = tubeNum[tubeNumCount - 1];
        }
    }
}

/**
  *@brief 延时
  *@param[in]
  *@param[out]
  *@return
  */
void Delay()
{
    int i = 0, j = 0;
    for(i;i<30000;i++)
    {
        for(j;j<30000;j++);
    }
}

关于亮度问题

数码管(二极管)的亮度取决于电流停留的时长,时长越短就越暗(类似于PWM波的占空比原理),所以,当扫描过快时,每一次停留时长较短,数码管就没那么亮。

解决方法是,适当地增加延时。

人眼感觉不到卡顿的界限通常是25Hz,比如电影就是1秒24帧或者25帧画面。也就是说,视觉暂留的理想时间是0.04秒。只要在0.04秒内再次出现,就可以实现扫描效果。

标签: 连接器j15放大电路数码管

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台