51单片机外设篇:数码管

本文详细介绍了数码管的工作原理,包括共阴极和共阳极的区别,以及如何通过单片机控制数码管静态和动态显示。动态显示利用视觉暂留效应,通过快速切换不同数码管的显示内容实现。此外,还讲解了74LS138译码器在减少引脚使用中的作用,并展示了代码优化技巧,以减少重复代码并提高效率。
摘要由CSDN通过智能技术生成

数码管简介

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时间;

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

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

静态数码管原理图

数码管本质上就是8个LED灯组成的。操作方式和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个数码管从前到后显示12345678,那么先第1个数码管显示1,然后第2个数码管显示2,然后第3个数码管显示3……第8个数码管显示8,因为代码运行的速度太快,人眼跟不上变化,所以看起来就是同时显示的。实现代码如下:

暂时还写不了代码,因为我在开发板上找J16找了好久,才发现J16和J15用跳帽连接了起来,而J15引脚连接了一个74LS138译码器。

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

74LS138 为3 线-8 线译码器,共有 54/74S138和 54/74LS138 两种线路结构型式,其74LS138工作原理如下:当一个选通端(G1)为高电平,另两个选通端(/(G2A)和/(G2B))为低电平时,可将地址端(A、B、C)的二进制编码在一个对应的输出端以低电平译出。

下图是它的原理结构图以及真值表:

无论从逻辑图还是功能表我们都可以看到74LS138的八个输出管脚,任何时刻要么全为高电平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] 
  *@return
  */
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秒内再次出现,就可以实现扫描效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值