前言
介绍
什么是 LED点阵屏?
百度百科
1
| LED点阵屏通过LED(发光二极管)组成,以灯珠亮灭来显示文字、图片、动画、视频等,是各部分组件都模块化的显示器件,通常由显示模块、控制系统及电源系统组成。LED点阵显示屏制作简单,安装方便,被广泛应用于各种公共场合,如汽车报站器、广告屏以及公告牌等。
|
8*8的LED点阵屏的内部连接如下
优点
由上图可以知道,每根线都连接了8个LED灯,这样做的好处显而易见,可以只用16根线控制64个LED
缺点
缺点也很明显,当一盏灯点亮时,再点亮另外一个LED可能导致其它无关LED被误点亮,
如(1,1)点亮,再点亮(2,2),此时(1,1)、(1,2)、(2,1)、(2,2)都是亮的(电源共地或共阳时)
解决方法
简单的方法
每次只点亮一只LED
流程:先点亮一个LED(为了方便理解标为1),当要点亮下一只LED时,先熄灭当前LED(1),再点亮下一只LED(标为2),再熄灭(2),点亮(1)如此快速循环,由于人眼的视觉暂留,人看这个LED点阵屏就是两个LED一直亮着,这也是为什么有时用手机拍摄LED点阵屏时可能会看到灯光闪烁(LED点阵屏刷新频率较低)
如下图(网图,侵权将删除),由于闪烁导致拍摄的图片显示不全,实际上在人眼看到的是没有缺失的
想法
只是一个想法,感觉没有必要,也不知道也没有起到优化作用(不知道会不会减少闪烁,闪烁可以通过提高刷新频率来解决)
成果
本文将从一个STC89C51(51单片机)最小系统开始,使用Keli uv4和STC-ISP
- 点亮一盏LED(作为Hello World)
- 点亮全屏的8*8 LED点阵屏
- 逐个点亮8*8 LED点阵屏上的LED
- 单独点亮任意两个或两个以上在8*8 LED点阵屏上的LED
- 根据图片取模的数组点亮LED
本文的结果如下图GIF
开始
既然概念已经讲完了,那就开始把
材料准备
5V电源
单片机使用电压为5V
USB转ttl下载器
用于下载程序到单片机
51单片机最小系统-准备
常用的最小系统电路如下
由于我用不到复位开关,故省去
准备材料如下
STC89C51单片机一个
引脚图如下
对照图如下
10μF 电容(10V或更高V)一个
此电容用于单片机复位
10KΩ电阻一个
此电阻用于给上面的10微法电容充电,电容刚开始充电时复位端为高电平,充电完成复位端由高电平变成低电平,完成单片机复位
8MHz晶振一个(手头上这个频率的晶振比较多,你也可以用其他的,但是要改本文的程序)
20-25pF电容两个
我使用了贴片的电容
普通LED和1KΩ电阻一个(这两样东西可以省略,我一开始用于测试单片机)
实验准备
8X8 LED点阵屏一个
AMS1117-3.3一个(原因后面会讲)
用于LED供电
ss8550三极管若干
我用的是贴片的ss8550,因为体积比较小
ss8550为PNP型三极管
连接
连接分两个部分,为了确保单片机功能正常
- 第一个部分先在单片机上连接一盏LED灯,用于测试程序的编译、单片机的复位电路、程序下载和IO口工作是否正常
- 第二部分开始连接8*8的LED点阵屏,并完成前言部分写的目标
第一部分的连接
先完成最小系统的连接
单片机的引脚对照图在上面已经给过了
连接测试LED
取LED和1K电阻,我这里电阻连接了LED的负极
LED正极连接到VCC,电阻连接到单片机P1.0脚,电阻用于限流,因为VCC为5V
这样连接是因为stc89c51单片机灌电流为20mA,拉电流为230uA,
如果LED正极连到P1.0脚,电阻连GND,P1.0为高电平时驱动时仅能看到LED微亮
如果LED正极连接到VCC,电阻连接到单片机P1.0脚,P1.0为低电平时LED能正常发亮,而此时灌电流为2mA,小于20mA,可行
上面所提到的计算如下:
1 2 3 4
| 输入5V,电阻1K,LED要求为3V,则电阻要分掉2V,则 电流为U/R=(5-3)V/1000Ω=2V/1000Ω=2mA 即使LED要求2.5V,此时 电流为U/R=(5-2.5)V/1000Ω=2.5V/1000Ω=2.5mA
|
将单片机上的TX连接到USB转ttl下载器的RX,单片机上的RX连接到USB转ttl下载器的TX,GND连GND
程序编译
打开keli uvision4,新建C51项目,打开输出hex选项
编译以下程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include<reg52.h> #include<intrins.h>
sbit D0=P1^0;
void Delay500ms() { unsigned char i, j, k;
i = 2; j = 134; k = 152; do { do { while (--k); } while (--j); } while (--i); }
void bliker(unsigned int times) { unsigned int tc = 0; while(1) { D0 = 0; Delay500ms(); D0 = 1; Delay500ms(); tc++; if (tc == times) { break; } } }
void main(void) { bliker(3); while (1) { } }
|
程序下载
打开STC-ISP,选择STC89C51RC,串口号选择你的USB转ttl下载器
点击”打开程序文件”选择位于项目目录下的Objects文件夹下的编译输出的hex文件
点击检测MCU选项,单片机VCC再接上5V
看到输出后说明单片机最小系统搭建完成
断开单片机电源后即可点击”下载/编程“,再接上电源进行程序下载
因为此单片机只有上电时才能下载
下载完成后LED将会闪烁3次
第二部分的连接
AMS1117-3.3端口实物对照如下
ss8550端口实物对照如下
按照此图连接8X8 LED点阵屏
定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| sbit D0=P1^0;
sbit D1=P1^1; sbit D2=P1^2; sbit D3=P1^3; sbit D4=P1^4; sbit D5=P1^5; sbit D6=P1^6; sbit D7=P1^7; sbit D8=P3^2;
sbit E1=P2^0; sbit E2=P2^1; sbit E3=P2^2; sbit E4=P2^3; sbit E5=P2^4; sbit E6=P2^5; sbit E7=P2^6; sbit E8=P2^7;
|
解释
由于手上电阻没有多少个了,所以我使用ams1117-3.3将5V降为3.3V,与ss8550三极管直接相连,三极管再与点阵屏阳极相连,省去LED的限流电阻
点阵屏的阴极统一接到单片机IO上,而三极管的基极(B极)接到单片机IO上
这样保证LED驱动电流足够,又由于3.3V低于5V,连IO到三极管基极的限流电阻都不需要,同时确保每个LED能点亮
SS8550-PNP三极管当作开关使用,单片机引脚输出低电平,三极管导通,连接在y轴
我的连接
最小系统与点阵连接
三极管连接
正面
只点亮任意一个LED
我写了一个函数用于控制多个IO
P1 = 254^0为11111110即P1.0输出为0,这里接的是测试LED的阴极,如果P1.0输出为0,LED会亮
P1 = 254^1时为255,即11111111,即0xff,即P1.0到P1.7输出为1
P1 = 254^2时为252,即11111100,即P1.0、P1.1输出为0,其它为1
P1 = 254^4为250,即11111010,即P1.0、P1.2输出为0,其它为1,以此类推
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| unsigned int pow2(unsigned int n_in) { if (n_in == 0) { return 1; } else { unsigned char nnn, result_ = 1; for (nnn=0;nnn<n_in;nnn++) { result_ *= 2; } return result_; } } sbit D8=P3^2; void set_io(unsigned char x, unsigned char y, unsigned char set_state) { if (set_state < 2) { if (x == 7) { D8 = 1^set_state; P1 = 0xff; } else { D8 = 1; if (set_state) P1 = 254^pow2(x+1); else P1 = 0xff; }
if (set_state) P2 = 255^pow2(7-y); else P2 = 0xff; } }
|
使用时只需要指定坐标和状态,从0开始
程序编译
编译以下程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| #include<reg52.h> #include<intrins.h>
sbit D0=P1^0;
unsigned int pow2(unsigned int n_in) { if (n_in == 0) { return 1; } else { unsigned char nnn, result_ = 1; for (nnn=0;nnn<n_in;nnn++) { result_ *= 2; } return result_; } } sbit D8=P3^2; void set_io(unsigned char x, unsigned char y, unsigned char set_state) { if (set_state < 2) { if (x == 7) { D8 = 1^set_state; P1 = 0xff; } else { D8 = 1; if (set_state) P1 = 254^pow2(x+1); else P1 = 0xff; }
if (set_state) P2 = 255^pow2(7-y); else P2 = 0xff; } }
void Delay500ms() { unsigned char i, j, k;
i = 2; j = 134; k = 152; do { do { while (--k); } while (--j); } while (--i); }
void bliker(unsigned int times) { unsigned int tc = 0; while(1) { D0 = 0; Delay500ms(); D0 = 1; Delay500ms(); tc++; if (tc == times) { break; } } }
void main(void) { int a,b; bliker(3); for (a=0;a<8;a++) { for (b=0;b<8;b++) { set_io(b,a,1); Delay500ms(); } } set_io(0,0,0); while (1) { } }
|
程序下载
下载方法与上面一致,这里不再重复
结果
正确下载后将出现下面GIF显示画面
将测试每个LED是否能够点亮
显示位图
绘制图案
打开Windows自带画图
使用”铅笔”工具绘制
我的图案
如下(很小)
保存为BMP
LED点阵展示
先对图像进行取模(使用取模软件),得到数组
需要注意,只能使用8*8分辨率的图像
在线取模
得到
1 2
| const unsigned char col[] U8X8_PROGMEM = { 0xef,0xef,0xef,0xef,0xef,0xd7,0xbb,0x7d };
|
需要改成
1 2
| const unsigned char col[] = { 0xef,0xef,0xef,0xef,0xef,0xd7,0xbb,0x7d };
|
原理
上面的col数组中存储的其实是每个LED的亮灭状态,例如0xef其实是11101111,即第四个LED亮,
颜色反转后就是00010000,即第四个LED不亮,其它都亮
程序
位图数组读取程序在此
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void bmp_light(unsigned char data_input[], unsigned char convert) { unsigned char i,j; unsigned char data0 = 0; unsigned char data1 = 0; for (j=0;j<8;j++) { data0 = data_input[j]; for (i=0;i<8;i++) { if (convert) data1=(data0&0x80)?1:0; else data1=(data0&0x80)?0:1; data0 <<= 1; if (data1) set_io(i,j,1); } } }
|
完整程序如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| #include<reg52.h> #include<intrins.h>
sbit D0=P1^0;
unsigned int pow2(unsigned int n_in) { if (n_in == 0) { return 1; } else { unsigned char nnn, result_ = 1; for (nnn=0;nnn<n_in;nnn++) { result_ *= 2; } return result_; } } sbit D8=P3^2; void set_io(unsigned char x, unsigned char y, unsigned char set_state) { if (set_state < 2) { if (x == 7) { D8 = 1^set_state; P1 = 0xff; } else { D8 = 1; if (set_state) P1 = 254^pow2(x+1); else P1 = 0xff; }
if (set_state) P2 = 255^pow2(7-y); else P2 = 0xff; } }
void Delay500ms() { unsigned char i, j, k;
i = 2; j = 134; k = 152; do { do { while (--k); } while (--j); } while (--i); }
void bmp_light(unsigned char data_input[], unsigned char convert) { unsigned char i,j; unsigned char data0 = 0; unsigned char data1 = 0; for (j=0;j<8;j++) { data0 = data_input[j]; for (i=0;i<8;i++) { if (convert) data1=(data0&0x80)?1:0; else data1=(data0&0x80)?0:1; data0 <<= 1; if (data1) set_io(i,j,1); } } }
const unsigned char col[] = { 0xef,0xef,0xef,0xef,0xef,0xd7,0xbb,0x7d };
void main(void) { bliker(3);
while (1) { bmp_light(col, 0); } }
|
效果
最后
还可以弄个数字显示
1 2 3 4 5 6 7 8 9 10 11 12
| unsigned char num_find[10][8] = { {0x00,0x00,0x00,0xe0,0xa0,0xa0,0xa0,0xe0}, {0x00,0x00,0x00,0x40,0xc0,0x40,0x40,0xe0}, {0x00,0x00,0x00,0xe0,0x20,0xe0,0x80,0xe0}, {0x00,0x00,0x00,0xe0,0x20,0xe0,0x20,0xe0}, {0x00,0x00,0x00,0xa0,0xa0,0xe0,0x20,0x20}, {0x00,0x00,0x00,0xe0,0x80,0xe0,0x20,0xe0}, {0x00,0x00,0x00,0xe0,0x80,0xe0,0xa0,0xe0}, {0x00,0x00,0x00,0xe0,0x20,0x20,0x20,0x20}, {0x00,0x00,0x00,0xe0,0xa0,0xe0,0xa0,0xe0}, {0x00,0x00,0x00,0xe0,0xa0,0xe0,0x20,0xe0} };
|
在主程序加上下面代码,就能显示数字5,5可以改成0到9的任意一个
1
| bmp_light(num_find[5], 1);
|
效果如下
谢谢观看
EOF