目标:
- 一次显示一个数字:在数码管第三位显示6.
- 同时显示多个不同数字:在数码管前三位分别显示1, 2, 3.
一、认识数码管
LED数码管:数码管是一种简单、廉价的显示器,是由多个发光二极管封装在一起组成“8”字型的器件
1. 原理图
开发板原理图中的数码管和74HC138译码器
它们都是用来控制数码管显示的。
2. 引脚定义
由数码管的引脚定义可知,89C52开发板上的数码管是4位一体的,4个数码管共阴极,通过位选确定哪个位显示数字,通过段选确定显示什么数字。P07是高位,P00是低位。
这种做法节约了I/O口,但是限制了数码管的显示,因为在4个数码管中,无论几位显示,都会显示相同的数字。
事实真是这样吗?
事实上,我们可以利用数码管的余辉和人眼的视觉暂留来同时显示不同的数字。
3. 74HC138译码器:位选,3线减为8线
在数码管模块中,可以看到LED1~LED8是提供位选的
LED1~LED8由谁控制呢?
可以看到,在74HC138译码器的输出端接了LED1~LED8。这8个输出由P22, P23, P24三个I/O口控制。
A, B, C为输入端,G1, G2 ‾ A \overline{\text{G2}}\text{A} G2A, G2 ‾ B \overline{\text{G2}}\text{B} G2B 为使能端, Y0 ‾ \overline{\text{Y0}} Y0~ Y7 ‾ \overline{\text{Y7}} Y7为输出端。
G1接1、G2接0,它才可以工作。
输入端由低位到高位的顺序为C B A,CBA组成2进制数,这个2进制数转换成的10进制数决定哪个端口有效。如,组成000时,Y0有效,此时输出端为0111 1111;组成101时,Y5有效,此时输出端为1111 0111。
4. 74HC245双向数据缓冲器
OE
‾
\overline{\text{OE}}
OE是使能端,接地工作,接高电平不工作。
DIR选择方向,接高电平时,把数据从左往右缓冲过来,接低电平时,把数据从右往左读取过来。
简单来说,就是给P0送什么数据,它都会原封不动地在B0~B7输出。
二、静态数码管显示
打开Keil5,新建工程,添加main.c,插入头文件,编写主函数。
1. 普通方法
我们要想让第三位显示数字,就要让LED6=0。那么就要让74HC138译码器的 Y5 ‾ \overline{\text{Y5}} Y5=0,那就要让CBA=101,即P2_4=1, P2_3=0, P2_2=1.
要想显示数字6,就要让a, c, d, e, f, g为1,b和dp为0,即B0~B7=1011 1110。要想让B0~B7=1011 1110,就得让A7~A0=0111 1101,此时P0=0x7D。
代码如下:
#include <REGX52.H>
void main()
{
P2_4 = 1;
P2_3 = 0;
P2_2 = 1;
P0 = 0X7D;
while(1)
{
}
}
此时,由于引脚冲突,D3和D4也会亮,不用管。
2. 高级方法
通过定义和调用子函数实现数码管显示数字。
使用switch(){…case:…;break;}进行位选,使用数组进行段选。
代码如下:
#include <REGX52.H>
unsigned char NumTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void digital(unsigned char location, number)
{
switch(location)
{
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 = NumTable[number];
}
void main()
{
digital(7, 2);
while(1)
{
}
}
可以看到第七个数码管显示2.
三、动态数码管显示
要同时显示多个不同数字,就要让数码管不断扫描,所以数码管的位选和段选应该移入while(1)循环中。
void main()
{
while(1)
{
digital(1, 1);
digital(2, 2);
digital(3, 3);
}
}
此时1, 2, 3的位置有些错乱:
所以还要添加一个延时函数:
void Delay(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
在主函数中调用延时函数。当延时200毫秒时,可以看到1, 2, 3交替闪烁。
void main()
{
while(1)
{
digital(1, 1);
Delay(200);
digital(2, 2);
Delay(200);
digital(3, 3);
Delay(200);
}
}
延时改为20毫秒后,1, 2, 3闪得更快了。
延时改为2毫秒后,肉眼已经看不出1, 2, 3在闪了。
加入延时函数这种操作叫“消影”。单片机动态显示数字时,工作状态是:位选、段选、位选、段选、位选、段选。单片机的工作频率很快,在本次段选结束,下次位选开始时,数字会窜到下一位,因为下次的段选还没开始。
常见的消影方法是在每次段选之后"清零"。加入延时函数,延时1毫秒,并给段选赋值0x00.
完整代码如下:
#include <REGX52.H>
unsigned char NumTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void Delay(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void digital(unsigned char location, number)
{
switch(location)
{
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 = NumTable[number];
Delay(1);
P0 = 0x00;
}
void main()
{
while(1)
{
digital(1, 1);
// Delay(2);
digital(2, 2);
// Delay(2);
digital(3, 3);
// Delay(2);
}
}
四、数码管驱动方式
单片机直接扫描:硬件设备简单,但会耗费大量的单片机CPU时间。本文中的数码管显示方式就是单片机直接扫描。
专用驱动芯片:内部自带显存、扫描电路,单片机只需告诉它显示什么即可。