本文章 来自原创专栏《51单片机手把手教学》,讲解如何使用 Keil uVision
构建 51单片机 程序,发布文章并 会持续为已发布文章添加新内容! 每篇文章都经过了精打细磨!
↓↓↓通过下方对话框进入专栏主页↓↓↓
CSDN 请求进入专栏 _ O x
是否进入《51单片机手把手教学》?
确定
又称LED数码管(LED Segment Displays)是由多个发光二极管封装在一起组成一个“8”字型的发光器件。
常见的数码管如上图,这是一个三位八段数码管。八段指的是数码管上有8个发光体(数字“8”上有7个段 + 右下角一个小数点)
使数码管显示数字的方法就是控制不同的发光体来发光,达到显示不同数字的目的。方法与上节流水灯一致。八段数码管中八个LED发光体有两种接法:共阴极 和 共阳极。所谓共阴极,即8个LED灯的负极接在一起作为数码管的公共端,如下图。
对于共阴极数码管,公共极接地,其余对应极接高电平时,对应发光体被点亮。如上图,数码管将要显示数字7
。
数码管内部原理图简化如下:
假设数码管的a,b,c,d,e,f,g,dp
分别接到了P0.0, P0.1, P0.2, P0.3, P0.4, P0.5, P0.6, P0.7
则代码如下
以数字 "7"
为例:
#include "reg52.h"
sbit smg_a = P0^0;
sbit smg_b = P0^1;
sbit smg_c = P0^2;
sbit smg_d = P0^3;
sbit smg_e = P0^4;
sbit smg_f = P0^5;
sbit smg_g = P0^6;
sbit smg_dp = P0^7;
void main(){
smg_a = 1; // 点亮a段
smg_b = 1; // 点亮b段
smg_c = 1; // 点亮c段
smg_d = 0; // 如下,其余全熄灭
smg_e = 0;
smg_f = 0;
smg_g = 0;
smg_dp = 0;
while(1);
}
或者(仍以数字 "7"
为例:)
#include "reg52.h"
sbit smg_a = P0^0;
sbit smg_b = P0^1;
sbit smg_c = P0^2;
sbit smg_d = P0^3;
sbit smg_e = P0^4;
sbit smg_f = P0^5;
sbit smg_g = P0^6;
sbit smg_dp = P0^7;
void main(){
P0 = 0x00; //熄灭全部
smg_a = 1; // 点亮a段
smg_b = 1; // 点亮b段
smg_c = 1; // 点亮c段
while(1);
}
以数字 "7"
为例:
#include "reg52.h"
void main(){
P0 = 0x07; // 点亮a, b, c. 其余全熄灭
// 因为0x07对应二进制0b00000111。所以能点亮a, b, c. 其余全熄灭
while(1);
}
以字母 "H"
为例:
#include "reg52.h"
void main(){
P0 = 0x76; // 点亮b, c, e, f, h. 其余全熄灭
// 因为0x76对应二进制0b01110110。所以能点亮b, c, e, f, h. 其余全熄灭
while(1);
}
为什么要引入动态数码管,而不是多个静态数码管同时工作来显示多位数字?
答:节省单片机IO口资源
原理:视觉暂留
如果你还不够了解动态数码管的原理,请认真阅读下边的一段话:
动态数码管显示是由两个操作完成的:控制显示何种图案 和 控制显示在哪一位上
显示多位数字的原理是,但数码管先将第一位数字显示出想要显示的目标数字。显示维持一段时间后将其熄灭,之后在很短时间内,数码管切换到第二位数字,再迅速将其点亮显示出目标数字。维持一段时间后再次将其熄灭,再切换到第三位数字上。以此类推...。这样数码管就 “同时” 显示出了多位数字。
那么具体怎么操作呢?我们拿下边的电路图来举例:
如果你认真阅读并理解了前文数码管的原理,那么你就不难理解下一句话:
控制显示在哪一位上,只需要控制电路顶端 LEDx电平的高低 即可。如
LED7
为低电平,其余的LEDx
(x ≠ \neq = 7)为高电平即可使来自P0的数据显示在第二位数码管上。
那么我们如何控制LEDx
电平的高低呢,需要结合电路图来分析,如果LEDx
直接接到了单片机的IO口上,直接操作IO即可。除此之外,我们可以利用38译码器
来在进一步节省单片机的IO口资源的前提下操纵LEDx
的电平。请往下看:38译码器
74HC138
芯片的应用
38译码器,能使用3个IO口即可达到8个IO口的效果。
真值表,表示的是逻辑芯片的输入与输出的逻辑关系。74HC138
的真值表(节选)如下:
例如我想要让LED2
为低电平,其余LEDx
(x ≠ \neq = 7) 均为高电平。则只需要Y1
为低电平,剩余为高电平即可。查阅真值表发现(A3 = 0; A2 = 0; A1 = 1
)即可。(0表示低电平,1表示高电平).
因此,通过如下代码即可操作38译码器的输入端,进而控制LEDx
。
例如:让LED2
为低电平,其余LEDx
(x ≠ \neq = 7) 均为高电平
#include "reg52.h"
sbit YMQ_A1 = P2^2;
sbit YMQ_A2 = P2^3;
sbit YMQ_A3 = P2^3;
...
...
YMQ_A1 = 0;
YMQ_A2 = 0;
YMQ_A3 = 1;
...
...
下图为38译码器模拟器,蓝色为低电平,橙色为高电平。前边3个管脚1测表示输入。后边8个管脚一侧为输出。与真值表一致
事实上,38译码器的输入输出是有规律的。
A3 A2 A1
连起来看成一个二进制数。如果A3 = 1, A2 = 0, A1 = 1
。组成二进制数字101
,对应十进制5
。则输出Y5为低电平,其余高电平。与真值表一致。
效果为:数码管从左到右显示 0 1 2 3 4 5 6 7
#include "reg52.h"
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
void delay(u16 i) {
while(i--);
}
void DigDisplay() {
u8 i;
for(i=0;i<8;i++)
{
switch(i) //位选,选择点亮的数码管,
{
case(0):
LSA=1;LSB=1;LSC=1; break;//显示第0位
case(1):
LSA=0;LSB=1;LSC=1; break;//显示第1位
case(2):
LSA=1;LSB=0;LSC=1; break;//显示第2位
case(3):
LSA=0;LSB=0;LSC=1; break;//显示第3位
case(4):
LSA=1;LSB=1;LSC=0; break;//显示第4位
case(5):
LSA=0;LSB=1;LSC=0; break;//显示第5位
case(6):
LSA=1;LSB=0;LSC=0; break;//显示第6位
case(7):
LSA=0;LSB=0;LSC=0; break;//显示第7位
}
P0=smgduan[i];//发送段码
delay(100); //间隔一段时间扫描
P0=0x00;//消隐,
}
}
void main() {
while(1) {
DigDisplay(); //数码管显示函数
}
}