前言
本文硬件采用普中A2开发板,单片机使用stc89c52rc,软件使用keil,烧录软件使用普中isp。
什么是数码管
数码管个人理解是以特殊方式连接排序的二极管,通过控制指定引脚输出合适电压控制指定灯光亮灭。
它长这样:
这是单位数码管其中封装了8个LED,通过控制指定LED内让他显示十六进制(0-F),当然也能显示其他其他各种奇形怪状的符号
除了单位的数码管还有多位的如下这种:
硬件连接
我们先来看一下数码管的端口链接
如图中的每一个字母对应一段LED,而每一段对应指定端口,通过程序选择指定段落亮起这便是段选
而像我们开发板上的不止一个数码管,有好多个如下图有八个
在选择要亮哪一位的结果这便是位选
在了解了段选和位选后我们来看看数码管与单片机的连接
数码管连接
如这图所示
要点亮8段LED至少需要8个引脚,为了节约引脚数码管将不断LED的正极的八个引脚或负极的八个引脚链接到一起用COM表示,分别是叫做共阳极数码管或是共阴极数码管。
共阴极/共阳极的选择 —— 共阴极的数码管将负极连接到一起,当哪一段的阳极为高电平时,就亮哪一段,而共阳极的数码管是将正极连接到一起,当哪一段的负极为底电平时,便亮哪一段
原理图
普中A2开发版原理图的数码管模块
如图所示在这块开发板上搭载的是两块四位的数码管,而两块数码管的段选端口又都连接到74HC245芯片上 在数码管显示系统中,
74HC245芯片主要用于扩展微控制器的I/O端口,增强信号的驱动能力,以及实现数据的双向传输。由于微控制器的I/O端口通常具有一定的负载能力,直接连接多个数码管可能会导致信号强度不足,影响显示效果。
74HC245通过其八路输出缓冲器,可以将微控制器的低电平信号放大,以驱动数码管的段选和位选,确保数码管能够正常显示。此外,74HC245的三态输出特性允许它在不干扰其他电路部分的情况下,通过使能端(OE)控制数据的输出。
该芯片的输入端连接到单片机的P00~P07口,与数码管中的LED段一一对应
那么在该开发版上如何实现位选呢,把开发版原理图往上拉一下能看到
该译码器能将3位的输入变成八位二进制的输出,在该开发板中用来选择数码管的位数
在该芯片的输入端口连接到单片机的P22~P24号引脚
驱动数码管
在知道驱动原理和结构后考虑一下如何控制数码管了
首先可以将八个控制端口连接到单片机上,在控制端口输出高低电平控制,十分简单,但缺陷也十分明显就是占用太多端口
其次还可以将上面讲到的74HC138译码器应用进来,把输出端与数码管依次相连再把输入端连接到ABC连接到单片机上,这样便可实现以三个端口控制八段的数码管,这样便能减少一般多的端口占用,可一个数码管占用3个端口似乎也不少,多加一个呢
嘢哎,似乎可以将两个数码管依照74HC138的输出端口串联起来,但这样似乎会信号强度不足,影响显示效果。这时候便可以使用74HC245芯片,该芯片功能上面也介绍过了通过其八路输出缓冲器,可以将微控制器的低电平信号放大,以驱动数码管的段选和位选,确保数码管能够正常显示。但这样另一个问题又出现了,连接了两个数码管该如何进行位选呢
我们可以像开发板一样讲两个数码管将串联到74HC138上以保证信号强度,再使用74HC245来对数码管进行位选
我们便按此开发板来尝试点亮一个数码管
我们需要先进行位选 ,在该开发板上的位选是由P22~P24号引脚连接至74HC138译码器上实现,所以我们只需要对P22到P24进行输出控制就好了,例如 对引脚进行以下设定便能在第一位数码管输出0
P2_4 = 1;
P2_3 = 1;
P2_2 = 1;
P0 = 0x3F; //对数码管进行段选 显示0
参照74HC138芯片的真值表 ABC位均为高电平时则对于Y0位为低电平
而如果要选择第二位数码管时候只需要对于111-1 也就是110 则Y1位为低电平 便能选择第二位了
P2_4 = 1;
P2_3 = 1;
P2_2 = 0;
P0 = 0x3F; //对数码管进行段选 显示0
P2_4引脚对应的是74HC138芯片的C号端口,
而P2_3引脚对应的是74HC138芯片的B号端口,
再P2_2引脚对应的是74HC138芯片的A号端口
想必你也发现了 引脚序号是从24开始的然后才23再是22 是倒叙的
完整的八位数码管选择表如下:
数码管编号 | C B A |
---|---|
1 | 111 |
2 | 110 |
3 | 101 |
4 | 100 |
5 | 011 |
6 | 010 |
7 | 001 |
8 | 000 |
根据这个表我们可以写一个方法函数来实现位选
void DigitSelector(int x){
switch(x){
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;
}
}
这个方法DigitSelector
是一个简单的选择器函数,用于根据输入的数字x
(范围从1到8)选择性地设置三个输出引脚P2_4、P2_3和P2_2的电平状态(即高电平1或低电平0)。
具体分析如下:
-
函数接收一个整型参数
x
,x
的值决定了P2_4、P2_3和P2_2这三个输出引脚的高低电平组合。 -
通过
switch
语句,函数根据x
的值执行不同的case
分支:case 1:
设置P2_4、P2_3和P2_2为高电平(1)。case 2:
设置P2_4和P2_3为高电平,P2_2为低电平(0)。case 3:
设置P2_4为高电平,P2_3和P2_2为低电平。case 4:
设置P2_4为高电平,P2_3和P2_2为低电平。case 5:
设置P2_3和P2_4为高电平,P2_2为低电平。case 6:
设置P2_3为高电平,P2_4和P2_2为低电平。case 7:
设置P2_2为高电平,P2_4和P2_3为低电平。case 8:
设置P2_4、P2_3和P2_2为低电平(0)。
-
当
case
语句执行完毕后,break
语句确保程序跳出switch
结构,防止代码执行到下一个case
。
这个函数可以用于控制7段数码管显示或其它需要二进制编码的设备,其中x
的值对应不同的显示数字或状态。
实现位选之后,就该控制显示的内容了
段选原理也是差不多通过设置端口高低电频实现
通过开发板原理图可以知道我们数码管的各段都连接到P00~P07引脚
在打开REGX52.H文件后,可以看到P0 Bit Registers如下
/*------------------------------------------------
P0 Bit Registers
------------------------------------------------*/
sbit P0_0 = 0x80;
sbit P0_1 = 0x81;
sbit P0_2 = 0x82;
sbit P0_3 = 0x83;
sbit P0_4 = 0x84;
sbit P0_5 = 0x85;
sbit P0_6 = 0x86;
sbit P0_7 = 0x87;
"P0 Bit Registers"通常指的是与微控制器的第一个端口(Port 0)相关联的单个位寄存器。这些寄存器允许程序员单独控制和读取连接到端口的每个引脚的状态。
不过通过这种方法来实现也太繁琐了可以通过操控P0字节寄存器来控制
不过需要通过十六进制来转换
如下通过P0显示阿拉伯数字6
void main()
{
while(1){
DigitSelector(1);
P0 = 0x7D;//0x7D = 0111 1101
};
}
对应的段显如下(要倒着看)
P0.1: 0 (低电平)
P0.2: 1 (高电平)
P0.3: 1 (高电平)
P0.4: 1 (高电平)
P0.5: 1 (高电平)
P0.6: 0 (低电平)
P0.7(最高位): 1 (高电平)
至于如何对应上呢 ……懒得写你一个一个对吧
既然知道原理了便可以写一个段码表了
段码 | 数码管显示的数字 |
---|---|
0x3F | 0 |
0x06 | 1 |
0x5B | 2 |
0x4F | 3 |
0x66 | 4 |
0x66 | 5 |
0x7D | 6 |
0x07 | 7 |
0x7F | 8 |
0x6F | 9 |
优化一下上面写的函数在实现位选的时候随便把段选也做了
void DigitSelector(int x,q){
char SegmentSelectArray[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
switch(x){
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端口进行设置
P0 = SegmentSelectArray[q];
}
添加了SegmentSelectArray的字符数组,该数组包含了10段数码管显示数字0到9所需的段编码。
我们来试试看
void main(){
while(1){
DigitSelector(1,1);
};
}
没问题通过掉用DigitSelector
函数实现了在第一位数码管显示阿拉伯数字1
当然也可以多次调用使得多个数码管亮起
如下
void main()
{
while(1){
DigitSelector(1,1);
DigitSelector(2,2);
DigitSelector(3,3);
};
}
但上面的除了指定点亮的段外其余的LED段还会发出微弱的光
我们只需要为它加上一定的延时就好了 它通过动态扫描 闪得太快了
void main()
{
while(1){
DigitSelector(1,1);
DelayMs(1);
DigitSelector(2,2);
DelayMs(1);
DigitSelector(3,3);
DelayMs(1);
};
}
如此便好了
这样就好多了
当然我们也可以把八个数码管点亮
如此本文到此介绍 数码管的使用并不难,不过是特殊排列的LED而已
可以利用不精确延时函数来做一个简单的计数器
担任可以显示的字符并不止0~9 还可以实现A ~ F 你需要一个一个的找出要亮的段
以及 我新建立的订阅号(E.F的杂笔)劳烦关注