51入门之数码管

目录

1.数码管硬件简介

        1.1数码管位选

        1.2数码管数选

2.静态数码管显示

3.动态数码管显示


1.数码管硬件简介

        数码管,在我们生活中无处不在,红绿灯的倒计时,电梯的显示屏,还有电子表的时间显示,都使用了数码管的相应原理。

        数码管分为两大类:共阴极和共阳极数码管,共阳极数码管就是阳极连接到同一接口处,阴极分开连接的一种结构,我们一般把公共端的输入叫做位选,把非公共端的输入叫做数选。

 

        比如我们需要这个共阳极数码管显示数字2,我们首先就要位选到这个数码管,也就是我们要把这个共阳极数码管的阳极输入高电平1,这个就叫做位选;然后就是数选,我们就要让这个数码管对应的a,b,g,e,d都接上低电平0,其它的接上高电平1,这样就完成了一个共阳极数码管显示数字2的操作。

        共阴极使用的方法正好和共阳极相反,共阴极使用阴极相接,我们位选数码管就只要把这个阴极置为低电平0即可,然后再进行数选。

        我们开发板上使用的是两个四位一体的共阴极数码管,下面是原理图:

        从原理图里我们可以知道,我们的数码管使用了两个芯片分别进行位选和数选。

        1.1数码管位选

        首先介绍74HC138芯片,我们把它称作138译码器,它是用来我们开发板上用来位选数码管的一个芯片,只需要输入3个引脚,就可以通过这三个引脚计算出一个二进制数,然后来确定哪个LED亮,这样的操作神乎其神,左边还有三个端口分别是G1,G2A,G2B,其中,G2A和G2B的头上有一个横线,表示低电平有效,G1没有这个横线就表示高电平有效,这几个就是用来确定芯片工作还是不工作的,这里G1接VCC,G1A和G2B接GND,直接表示芯片永久有效工作。所以,我们只需要关心这个P22到P24端口的输入就可以了。

        这里,我们的输入端口P22,P23,P24是和它们的输出反过来的:

比方说这个,用ABC分别表示P22,P23,P24的话,它的输入其实是需要反过来读的,比如说A = 0,B = 0, C = 1,我们需要把这里的001反过来,表示成100,也就是对应十进制的4,对应的就是Y4,LED5,所以这个4表示的其实是第5个数码管,也就是LED5,问题又来了,这里需要注意一下,我们开发板上的数码管从左到右是LED8到LED1,所以,我们的所谓”第五个数码管“其实是从右往左数第五个数码管;同样的,假设输入的是A = 1,B = 1,C = 0,表示的就是011,也就是Y3,LED4,就是从右往左数第四个数码管,以此类推。

        1.2数码管数选

数码管的数选需要考虑两部分,先说芯片部分,如图所示,我们使用的是74HC245芯片,VCC和GND就是正负极不需要解释,OE是使能引脚,上面有一个横线表示的就是低电平有效,然就就是DIR引脚,它表示的意思就是DIRECTION(方向),接正极的时候就是表示信号从左到右,接负极的时候就表示从右输到左,在开发板上就是以一个跳线帽的形式存在(在数码管右侧,名字叫J24),我们可以调整跳线帽使它接VCC或者是GND,当它接VCC的时候,这个数码管的数选才会有作用,否则输入的数据就是无效的。

       这个芯片A0到A7依次对应B0到B7,再对应a到g和dp,这样做的目的不为了什么,就是为了让这个从P0端口输入的数据只是一个信号,主要驱动数码管亮灭的能力在于B端口——这个经过了芯片处理,保证数码管的驱动是电源而不是微弱的信号,保证数码管亮度足够。

        其次我们之前说过了我们的开发板上的数码管其实是共阴极数码管,共阴极数码管的数选是要输入高电平驱动的,所以我们只要对P0进行操控就行了。

        而这里也是有一点要注意的,就是这里的P0输入还是类似LED,就像一个栈一样,我们输入1000 0000对应单片机得到的信号就是0000 0001,刚好是反过来的,所以我们想要实现对应的数字的话,我们的也要把这个输入反过来。

到目前为止我们在C51里面接触到了很多这样的倒过来存储的方式,可以猜测的是,这个是一个特定的存储方式,可以和Keil5使用的小端存储方式有关,我们平时使用的别的编译器使用的大部分是大端存储,这里就介绍一下大端存储和小端存储的区别:

大端存储(Big Endian)和小端存储(Little Endian)是指在多字节数据存储时,字节的排列顺序不同的两种方式。

  • 大端存储(Big Endian):

在大端存储中,多字节数据的最高有效字节(Most Significant Byte,MSB)存储在最低地址,而最低有效字节(Least Significant Byte,LSB)存储在最高地址。
举例来说,十六进制数0x12345678,在大端存储中会被存储为:0x12 0x34 0x56 0x78。

  • 小端存储(Little Endian):

在小端存储中,多字节数据的最低有效字节(LSB)存储在最低地址,而最高有效字节(MSB)存储在最高地址。
举例来说,十六进制数0x12345678,在小端存储中会被存储为:0x78 0x56 0x34 0x12。

  • 形象例子:

想象一列连续排列的房子,每个房子里有一位数字。这些数字组成一个大的十进制数。现在考虑将这个数写在一张纸上,而你选择的写入顺序就代表了字节序。

大端存储:你从最高位数字开始写,然后逐渐向低位数字写。
小端存储:你从最低位数字开始写,然后逐渐向高位数字写。
假设这个数是12345678,那么在大端存储中,写出的顺序是12345678;而在小端存储中,写出的顺序是87654321。

所以,我们就会经常在51单片机里接触到这样的数据倒过来存储的方式

(以上大端存储和小端存储的知识都是正确的,但是具体这些数据的输入是不是和这个有关系还暂时只是个人的一个猜测,并没有相应的考究)

2.静态数码管显示

        静态数码管的实现很简单,就是我们前面示范的那样使用位选+数选即可,掌握上面的两个芯片的对应关系,其实还是比较简单的,这里主要是需要使用数组和函数把我们的位选和数选简化成简单的一个接口函数,我们只要向接口函数里输入位置参数和数字参数,就可以实现在数码管对应 的位上显示数字的效果。

        首先我们可以配置一下这个数码管显示的数字对应的十六进制码,使用数组存储,这里我们不止可以让我们的数码管输出数字,还可以让它输出英文。从A到F,刚好可以完整表示一个十六进制可以表示的数。

        按照顺序的话,我们的对应的就是

数字 0: 0x3F
数字 1: 0x06
数字 2: 0x5B
数字 3: 0x4F
数字 4: 0x66
数字 5: 0x6D
数字 6: 0x7D
数字 7: 0x07
数字 8: 0x7F
数字 9: 0x6F
数字 A: 0x77
数字 B: 0x7C
数字 C: 0x39
数字 D: 0x5E
数字 E: 0x79
数字 F: 0x71

 我们把它们写进数组里面,就可以得到:

const unsigned char SMG[] = {
    0x3F, // 0
    0x06, // 1
    0x5B, // 2
    0x4F, // 3
    0x66, // 4
    0x6D, // 5
    0x7D, // 6
    0x07, // 7
    0x7F, // 8
    0x6F, // 9
    0x77, // A
    0x7C, // B
    0x39, // C
    0x5E, // D
    0x79, // E
    0x71  // F
};

然后我们需要实现函数接口,第一个参数为位,第二个参数为数,函数实现如下:

const unsigned char SMG[] = {
    0x3F, // 0
    0x06, // 1
    0x5B, // 2
    0x4F, // 3
    0x66, // 4
    0x6D, // 5
    0x7D, // 6
    0x07, // 7
    0x7F, // 8
    0x6F, // 9
    0x77, // A
    0x7C, // B
    0x39, // C
    0x5E, // D
    0x79, // E
    0x71  // F
};

void SMGDisPlay(unsigned char loc,unsigned char num)
{
	num%=16;
	switch (loc)
	{
		case 1:
			P2_4 = 1;
			P2_3 = 1;
			P2_2 = 1;
			break;
		case 2:
			P2_4 = 1;
			P2_3 = 1;
			P2_2 = 0;
			break;
		case 3:
			P2_4 = 1;
			P2_3 = 0;
			P2_2 = 1;
			break;
		case 4:
			P2_4 = 1;
			P2_3 = 0;
			P2_2 = 0;
			break;
		case 5:
			P2_4 = 0;
			P2_3 = 1;
			P2_2 = 1;
			break;
		case 6:
			P2_4 = 0;
			P2_3 = 1;
			P2_2 = 0;
			break;
		case 7:
			P2_4 = 0;
			P2_3 = 0;
			P2_2 = 1;
			break;
		case 8:
			P2_4 = 0;
			P2_3 = 0;
			P2_2 = 0;
			break;
	}
	P0 = SMG[num];
}

        这样就算大功告成了,我们只要输入3,8就表示”在第三个数码管显示数字8“.在主函数调用一下就好了。

3.动态数码管显示

        动态数码管显示的原理就是静态数码管的变化版,我们需要实现一个动态的数码管显示,比如在一个数码管上从1数到9,间隔0.5s,我们只要在每次调用我们刚刚为静态数码管写的函数之后加上一个Delay暂停函数就好了。

        具体实现如下:

#include <REGX52.H>
#include <INTRINS.H>

void Delay(int xms)		//@11.0592MHz
{
	while(xms--)
	{
			unsigned char i, j;
			_nop_();
			i = 2;
			j = 199;
			do
			{
				while (--j);
			} while (--i);
	}
}
const unsigned char SMG[] = {
    0x3F, // 0
    0x06, // 1
    0x5B, // 2
    0x4F, // 3
    0x66, // 4
    0x6D, // 5
    0x7D, // 6
    0x07, // 7
    0x7F, // 8
    0x6F, // 9
    0x77, // A
    0x7C, // B
    0x39, // C
    0x5E, // D
    0x79, // E
    0x71  // F
};

void SMGDisPlay(unsigned char loc,unsigned char num)
{
	num%=16;
	switch (loc)
	{
		case 1:
			P2_4 = 1;
			P2_3 = 1;
			P2_2 = 1;
			break;
		case 2:
			P2_4 = 1;
			P2_3 = 1;
			P2_2 = 0;
			break;
		case 3:
			P2_4 = 1;
			P2_3 = 0;
			P2_2 = 1;
			break;
		case 4:
			P2_4 = 1;
			P2_3 = 0;
			P2_2 = 0;
			break;
		case 5:
			P2_4 = 0;
			P2_3 = 1;
			P2_2 = 1;
			break;
		case 6:
			P2_4 = 0;
			P2_3 = 1;
			P2_2 = 0;
			break;
		case 7:
			P2_4 = 0;
			P2_3 = 0;
			P2_2 = 1;
			break;
		case 8:
			P2_4 = 0;
			P2_3 = 0;
			P2_2 = 0;
			break;
	}
	P0 = SMG[num];
}

void main()
{
	unsigned char i =0;
	while(1)
	{
			for(i = 1;i <= 9 ;i++)
			{
				SMGDisPlay(1,i);
				Delay(500);
			}
	}
}

然后就成功显示了。

        接下来我们会遇到一个问题:怎么一次性显示多个数字呢?其实很简单,类似这种显示都是使用快速刷新来实现的(电脑,电视,手机等),我们可以把暂停间隔减小到很小,直至我们人眼无法看见,循环播放想要显示的几个位上的数字,这样我们就成功一次显示多个数字了。

        我们代码写出来是下面这样:

void main()
{
	while(1)
	{
			SMGDisPlay(1,1);
			SMGDisPlay(2,2);
			SMGDisPlay(3,3);
	}
}

        没有一点问题,就是按照上面的思路实现的,甚至整个时间间隔几乎没有,应该显示的更加流畅才是。但是我们看看效果:

        数字模糊不清,不是摄像头的问题,本身就是这样的,根本很难看出来数码管显示的数字。

        这样的现象我们叫做数码管的”影“,也就是数码管很常见的问题,我们稍微研究一下数码管显示的工作原理就不难发现,其实数码管操作就是由位选和数选组成的。当我们多个位选和数选没有间隔的连接在一起,比如:位选,数选,位选,位选,数选,我们会发现,中间其实会有这样的现象:第一次的数选后到第二次的数选前,正好是第一次的数选和第二次的位选相互组合,就变成了用第二次的位显示第一次的数,但是我们原本想要的其实是第二次的位就匹配第二次的数,所以我们这里出现了这样的现象。

        想要消除这样的现象,我们一般把它叫做”数码管的消隐“,就是我们每次位选+数选一遍之后,都把我们前面的数选清零,然后再进行下一次的循环,这样我们就得到了下面的代码和效果:
代码:

void main()
{
	while(1)
	{
			SMGDisPlay(1,1);
			
			P0 = 0X00;
		
			SMGDisPlay(2,2);
			
			P0 = 0X00;
		
			SMGDisPlay(3,3);
			
			P0 = 0X00;
	}
}

现象:

没有出现错乱的情况了,但是亮度感觉有点不够,所以我们可以在每次函数调用之后,数选清零之前加一个小小的Delay,延长一下显示的时间占比,从而提高显示的亮度

代码:

void main()
{
	while(1)
	{
			SMGDisPlay(1,1);
			Delay(1);
			P0 = 0X00;
		
			SMGDisPlay(2,2);
			Delay(1);
			P0 = 0X00;
		
			SMGDisPlay(3,3);
			Delay(1);
			P0 = 0X00;
	}
}

现象:

肉眼可见亮了不少。

        其实还有这样一种情况,这里就简单描述一下: 就是我们只使用Delay而不使用把数选重新清零的情况下,还是可以显示似乎”很正常“的现象,但其实只要你认真看,它的非发光的其他位置还是有一点点的亮度的,也就是说这些地方还是亮的,只是我们使用Delay之后看起来我们想要的显示的很亮,就没有注意到其实还有很多其他的不该亮的地方也是亮着的,只是较为微弱,总而言之,这样还是不对的,所以我们还是需要数选清零这个操作来”消隐“。

        为了更方便以后我们可以抄写我们的代码,而不用消隐我们其实还可以把我们的代码改造一下:

void SMGDisPlay(unsigned char loc,unsigned char num)
{
	P0 = 0X00;
	num%=16;
	switch (loc)
	{
		case 1:
			P2_4 = 1;
			P2_3 = 1;
			P2_2 = 1;
			break;
		case 2:
			P2_4 = 1;
			P2_3 = 1;
			P2_2 = 0;
			break;
		case 3:
			P2_4 = 1;
			P2_3 = 0;
			P2_2 = 1;
			break;
		case 4:
			P2_4 = 1;
			P2_3 = 0;
			P2_2 = 0;
			break;
		case 5:
			P2_4 = 0;
			P2_3 = 1;
			P2_2 = 1;
			break;
		case 6:
			P2_4 = 0;
			P2_3 = 1;
			P2_2 = 0;
			break;
		case 7:
			P2_4 = 0;
			P2_3 = 0;
			P2_2 = 1;
			break;
		case 8:
			P2_4 = 0;
			P2_3 = 0;
			P2_2 = 0;
			break;
	}
	P0 = SMG[num];
	Delay(1);
}

        这样的表示就很巧妙,我们在函数开头的地方加上了一个数选清零用来消隐,最后面加上了一个Delay函数用来实现我们的”增加占比时间“使数码管亮度增加,并且这样的使用在我们只有一个数码管的时候也是适用的,简直可以称作天才般的融合!哈哈,其实最好还是不要这样写,因为我们刚入门,所以需要多次实践”消隐“这个步骤,以免忘记这个重要的操作,所以我们还是使用原来的函数多使用几次,等完全掌握并记住了”消隐“操作之后,再使用后面这个优化后的代码也不是不可以的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/516302.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大厂面试之【Redis持久化机制】 - RDB和AOF概述及应用配置

文章目录 Redis持久化1. RDB(Redis DataBase)1.1 概述1.2 配置应用 2. AOF(Append Only File)2.1 概述2.2 配置应用 Redis持久化 先上结论&#xff1a;Redis持久化操作分为rdb以及aof&#xff0c;但是前者已经够用 1. RDB(Redis DataBase) 1.1 概述 rdb保存的是dump.rdb文件在指…

31. UE5 RPG使用增强输入激活GameplayAbility(一)

在前面文章中&#xff0c;我们实现了对技能添加并直接激活功能&#xff0c;介绍了GA的相关参数配置。现在&#xff0c;我们还不能通过键位触发技能&#xff0c;正常在游戏时&#xff0c;我们需要通过键位触发技能&#xff0c;实现技能的激活。 在UE5里面添加了增强输入&#xf…

modprobe加载驱动模块时报错:modprobe: module xxx.ko not found in modules.dep

问题 使用modprobe时&#xff0c;报错modprobe: module xxx.ko not found in modules.dep&#xff1a; 原因 加载模块时&#xff0c;依赖没法正确添加 解决 在使用modprobe前&#xff0c;调用一下depmod指令&#xff0c;之后再用modprobe加载驱动模块 depmod modprobe interr…

使用Autodl与Xftp远程训练模型及管理远程文件

1 AutoDL网站登录创建实例 AutoDL网站:AutoDL算力云 | 弹性、好用、省钱。租GPU就上AutoDL 1&#xff09;进入算力市场&#xff0c;选取可用显卡&#xff08;工作日一般白天抢不到&#xff0c;晚上才能抢到&#xff09; 2&#xff09;选择配置环境 3&#xff09;创建成功实例…

基于ArrayList实现简单洗牌

前言 在之前的那篇文章中&#xff0c;我们已经认识了顺序表—>http://t.csdnimg.cn/2I3fE 基于此&#xff0c;便好理解ArrayList和后面的洗牌游戏了。 什么是ArrayList? ArrayList底层是一段连续的空间&#xff0c;并且可以动态扩容&#xff0c;是一个动态类型的顺序表&…

关于hive启动的相关问题记录

问题&#xff1a;初始化hive元数据报错 [atguiguhadoop102 software]$ schematool -initSchema -dbType mysql -verboseError: Table CTLGS already exists (state42S01,code1050) Closing: 0: jdbc:mysql://hadoop102:3306/metastore?useSSLfalse org.apache.hadoop.hive.me…

数据结构和算法:十大排序

排序算法 排序算法用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用&#xff0c;因为有序数据通常能够被更高效地查找、分析和处理。 排序算法中的数据类型可以是整数、浮点数、字符或字符串等。排序的判断规则可根据需求设定&#xff0c;如数字大小、字符 ASCII…

Linux文件管理,压缩/解压缩和vi编辑器

目录 一.linux文件管理 1.cat 2. head和tail 3.WC 4.grep 5.paste&#xff08;合并&#xff09; 二.压缩,解压缩 1.gzip bzip2 2.归档tar 3.zip 和unzip 三.vi编辑器 一.linux文件管理 1.cat 格式 &#xff1a;cat 选项 文件名 &#xff08;文件名可以是单…

哈佛大学商业评论 --- 第三篇:真实世界中的增强现实

AR将全面融入公司发展战略&#xff01; AR将成为人类和机器之间的新接口&#xff01; AR将成为人类的关键技术之一&#xff01; 请将此文转发给您的老板&#xff01; --- 本文作者&#xff1a;Michael E.Porter和James E.Heppelmann 虽然物理世界是三维的&#xff0c;但大…

软件杯 深度学习中文汉字识别

文章目录 0 前言1 数据集合2 网络构建3 模型训练4 模型性能评估5 文字预测6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习中文汉字识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xf…

代码随想录第30天|51. N皇后

51. N皇后 51. N 皇后 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 这就是传说中的N皇后&#xff1f; 回溯算法安排&#xff01;| LeetCode&#xff1a;51.N皇后_哔哩哔哩_bilibili 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行…

mysql+keepalive+lvs搭建的数据库集群实验

前提条件&#xff1a;准备5台计算机&#xff0c;且网络互通 1、客户端 yum groups -y install mariadb-client ip 192.168.0.5 2、lvs1 yum-y install ipvsadm keepalived ip 192.168.0.1 keepalivedvip 192.168.0.215 /etc/hosts 解析192.168.0.1 主机名 3、lvs2 yum-y i…

大数据实验三-HBase编程实践

目录 一&#xff0e;实验内容 二&#xff0e;实验目的 三&#xff0e;实验过程截图及说明 1、安装HBase 2、配置伪分布式模式&#xff1a; 3、使用hbase的shell命令来操作表&#xff1a; 4、使用hbase提供的javaAPI来编程实现类似操作&#xff1a; 5、实验总结及心得体会…

uniApp使用uview对vuex的二次封装实现全局变量

1、uni-app目根目录新建’/store/index.js’&#xff0c;并复制如下内容到其中 2、uni-app目根目录新建’/store/ u . m i x i n . j s ′ &#xff0c;并复制如下内容到其中&#xff0c;由于 H X 某些版本的限制&#xff0c;我们无法帮您自动引入 " u.mixin.js&#xff0…

不堪大用的pow

【题目描述】 输出100&#xff5e;999中的所有水仙花数。若3位数ABC满足&#xff0c;则称其为水仙花 数。例如&#xff0c;所以153是水仙花数。 【题目来源】 刘汝佳《算法竞赛入门经典 第2版》习题2-1 水仙花数&#xff08;daffodil&#xff09; 题目很简单&#xff0c;…

指针的偏移遍历数组--指针和数组名的区别

1.指针取地址&#xff1a;可以是数组名&#xff0c;可以是数组首地址&arr[0] 2.指针偏移完后记得回到数组首地址 #include <stdio.h>int main(){int arr[3] {1,2,3};int *p;int i;p arr; // 数组名就是数组的首地址// p &arr[0] 数组的首地址就是首个元素…

二分答案跳石头游戏

步骤&#xff1a; 输入&#xff1a; 用户输入了三个整数&#xff0c;分别表示石头的总长度l&#xff0c;石头的数量n&#xff0c;以及最多可以撤去的石头数量m。 初始化石头位置数组&#xff1a; 创建一个长度为n2的数组arr&#xff0c;用于存储每块石头的位置。数组的第一项…

FreeRTOS作业day4

1.总结二进制信号量和计数型信号量的区别&#xff0c;以及他们的使用场景。 二进制信号量的数值只有0和1&#xff0c;用于共享资源的访问 计数型信号量的值一般是大于或者等于2&#xff0c;用于生产者和消费者模型 2.使用技术型信号量完成生产者和消费者模型实验。 void Sta…

使用 ChatGPT 集成精通高级 Excel(二)

原文&#xff1a;Mastering Advanced Excel - With ChatGPT Integration 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第九章数据透视表 介绍 数据透视表是一种基于交互式工作表的表格&#xff0c;可以快速汇总大量数据&#xff0c;使用您选择的格式和计算方法。它…

AI论文速读 | 2024[WWW]不只是路线:联合 GPS 和路线建模的轨迹表示学习

论文标题&#xff1a;More Than Routing: Joint GPS and Route Modeling for Refine Trajectory Representation Learning 作者&#xff1a;Zhipeng Ma&#xff08;麻志鹏&#xff09;, Zheyan Tu, Xinhai Chen, Yan Zhang, Deguo Xia, Guyue Zhou, Yilun Chen, Yu Zheng&…