数码管可以说是显示屏的一种, 通过控制其不同管脚的电流通断,会使其发光,从而显示出数字。因此它能够显示时间、日期、温度等所有可用数字表示的信息。由于它的价格便宜,使用简单,在电器特别是家电领域应用极为广泛,空调、热水器、冰箱等等。绝大多数需要显示的小家电都是使用数码管,再复杂一点的应用则会采用液晶屏与荧光屏等。
数码管结构数码管也称LED数码管,常用段数一般为7段,也有的另加一个小数点。也就是说一位数字就需要用到8个LED小灯。数码管根据LED的接法不同分为共阴和共阳两类:
共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳数码管在应用时应将公共极COM接到 5V,当某一字段发光二极管的阴极为低电平时,相应字段就点亮,当某一字段的阴极为高电平时,相应字段就不亮。
共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码管,共阴数码管在应用时应将公共极COM接到地线GND上,当某一字段发光二极管的阳极为高电平时,相应字段就点亮,当某一字段的阳极为低电平时,相应字段就不亮。
当数码管特定的段加上电压后,这些特定的段就会发亮,以形成我们眼睛看到的字样了。如:显示一个“2”字,那么应当是A亮B亮G亮E亮D亮;F不亮C不亮dp不亮。因此常用LED数码管可以显示的数字和字符是0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F。
当然,也有更加复杂一点的数码管,如下图,我们就不详加介绍了。
驱动方式想要驱动数码管显示出我们想要的数字其实非常简单,只要对不同的LED段加上相应的电压就行。但是想象一下,如果需要显示下图的时间13点08分,我们需要控制20个LED,使其亮;12个LED,使其灭。每个LED接一条线路,就要接32条,这样其实是十分浪费资源的。
因此数码管的驱动方式十分灵活,可以分为静态驱动和动态驱动两类。
静态显示驱动:
静态驱动也称直流驱动。静态驱动是指每个数码管的每一个段码都由一个单片机的I/O端口进行驱动,或者使用如BCD码二-十进制译码器译码进行驱动。静态驱动的优点是编程简单,显示亮度高,缺点是占用I/O端口太多,如驱动5个数码管静态显示则需要5×8=40根I/O端口来驱动。要知道一个89S51单片机可用的I/O端口才32个,实际应用时必须增加译码驱动器进行驱动,增加了硬件电路的复杂性。
动态显示驱动:
数码管动态显示接口是单片机中应用最为广泛的一种显示方式之一,动态驱动是将所有数码管的8个显示笔划"a,b,c,d,e,f,g,dp"的同名端连在一起,另外为每个数码管的公共极COM增加位选通控制电路,位选通由各自独立的I/O线控制,当单片机输出字形码时,所有数码管都接收到相同的字形码,但究竟是哪个数码管会显示出字形,取决于单片机对位选通COM端电路的控制,所以我们只要将需要显示的数码管的选通控制打开,该位就显示出字形,没有选通的数码管就不会亮。通过分时轮流控制各个数码管的的COM端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的I/O端口,而且功耗更低。
现实应用中,绝大部分的数码管都是动态显示的,只是人眼看不出效果而已。但我们可以借助手机来验证这一点。当我们将快门速度调慢,拍下的数码管照片就是完整的显示数字;当快门速度调快到一定速度时,拍下的显示数字就是不完整的,这也就说明了这个数码管是采用动态显示的驱动方式。
模块介绍下面我们来着重介绍一下一个八位的LED数码管模块的结构和驱动方式。
一个八位的数码管总共有64个LED,我们不可能用64个I/O引脚去控制,因此需要一个驱动芯片。这个模块采用的是MAX7219芯片。它是一种集成化的串行输入/输出共阴极显示驱动器。它连接微处理器与8位数字的7段数字LED显示,也可以连接条线图显示器或者64个独立的LED。其上包括一个片上的B型BCD编码器、多路扫描回路,段字驱动器,而且还有一个8*8的静态RAM用来存储每一个数据。只需要3个I/O口就能驱动8位数码管,可以说效率是很高了。整个模块的电路图如下:
主要参数:
- 工作电压:DC 5V
- 规格尺寸:82*15mm
引脚定义:
- VCC:接电源正极
- GND:接电源负极
- DIN:串行数据输入端口
- CS:位选端
- CLK:时钟序列输入端
下面我们就用Arduino UNO驱动8位数码管模块工作吧。
硬件设备:
- Arduino UNO控制器 × 1
- 8位数码管模块 × 1
- 电源 × 1
- 杜邦线 × n
接线图:
将驱动模块的DIN、CS、CLK分别连到Arduino 12、11、10数字引脚上,连接电源和接地线。
程序:
在编程之前我们需要知道一位数码管对应显示各个数字字符的段码。我们可以用这样的一个小程序进行查询。例如数字2,它的段码正码就是5B,反码就是A4,分别对应共阳和共阴的数码管。
//数码管显示程序
//事先输入查询好的各个字符的段码
uint8_t SEGPLAY[] = { 0xC0, //"0"
0xF9, //"1"
0xA4, //"2"
0xB0, //"3"
0x99, //"4"
0x92, //"5"
0x82, //"6"
0xF8, //"7"
0x80, //"8"
0x90, //"9"
// 0x88, //"A"
// 0x83, //"B"
// 0xC6, //"C"
// 0xA1, //"D"
// 0x86, //"E"
// 0x8E, //"F"
// 0x89, //"H"
// 0xC7, //"L"
// 0xC8, //"n"
// 0xC1, //"u"
// 0x8C, //"P"
// 0xA3, //"o"
// 0xBF, //"-"
// 0xFF, //熄灭
// 0xFF //自定义
};
//定义位码,也就是1到8位的数码管的代码
uint8_t DIG[] = {0b10000000,
0b01000000,
0b00100000,
0b00010000,
0b00001000,
0b00000100,
0b00000010,
0b00000001,
};
//定义三个引脚
#define latchPin 10
#define clockPin 11
#define dataPin 12
void setup() {
//定义三个引脚为输出
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
}
void loop()
{
playnum (1308, 5014); //显示 13085014
delay(20);
}
//
//函数作用:用于两个四位数码管显示数字
//输入值:unsigned int 范围0-9999 //
//返回值: 无 //
void playnum (unsigned int i, unsigned int j)
{
SegDisplay(i / 1000, 0);
SegDisplay((i % 1000) / 100, 1);
SegDisplay((i % 100) / 10, 2);
SegDisplay((i % 10), 3);
SegDisplay( j / 1000, 4);
SegDisplay((j % 1000) / 100, 5);
SegDisplay((j % 100) / 10, 6);
SegDisplay((j % 10), 7);
}
//
//函数作用:用于单位数码管显示数字
//输入值:i 范围0-11 //
// 显示位置 1-4 //
//返回值: 无 //
void SegDisplay(int i, int j)
{
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, LSBFIRST, DIG[j]); //位选
shiftOut(dataPin, clockPin, MSBFIRST, SEGPLAY[i]);//段选
digitalWrite(latchPin, HIGH);
delayMicroseconds(500);//调节这个和下面的参数可以调整显示亮度,建议这两个数加起来为860。这样是为了显示时间准确
// delayMicroseconds(360);
}
,