一、目标
- LED点阵屏显示静态图案
- LED点阵屏显示滚动字幕
二、LED点阵屏和74HC595介绍
LED点阵屏由若干个独立的LED组成,LED以矩阵的形式排列,以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种公共场合,如汽车报站器、广告屏以及公告牌等。
LED点阵屏分类:
- 按颜色:单色、双色、全彩
- 按像素:8*8、16*16等(大规模的LED点阵通常由很多个小点阵拼接而成)
可以看到普中A2开发板LED点阵模块,由P0和DP控制。
DP是74HC595输出的。
因为OE上面有一个横线,代表只有OE为低电平时,74HC595才能工作。想让74HC595工作,就得把OE接地。即:把点阵屏左上角的跳线帽放到左边两个针上。
74HC595是串行输入并行输出的移位寄存器,可用3根线输入串行数据,8根线输出并行数据,多片级联后,可输出16位、24位、32位等,常用于IO口扩展。
RCLK:表示寄存器时钟,上升沿锁存
SRCLR
‾
\overline{\text{SRCLR}}
SRCLR:表示串行清零
SRCLK:表示串行时钟,上升沿移位
SER:用于输入串行数据
QH’:表示多片级联。若有多片级联,则接到下一片的SER
主要通过SER、SRCLK、RCLK控制。
三、LED点阵屏显示图形
sfr(special function register):特殊功能寄存器声明
例:sfr P0 = 0x80;
声明P0口寄存器,物理地址为0x80
sbit(special bit):特殊位声明
例:sbit P0_1 = 0x81;
或sbit P0_1 = P0^1;
声明P0寄存器的第1位
在main.c中,定义一下要用的三个位。
#include <REGX52.H>
sbit RCLK = P3^5;
sbit SRCLK = P3^6;
sbit SER = P3^4;
void main()
{
while(1)
{
}
}
编译一下,发现报错:RCLK重定义了。
main.c(3): error C231: 'RCLK': redefinition
打开REGX52.H的184行,发现
sbit RCLK = 0xCD;
所以,把main.c中的RCLK改为RCK。
为了简洁,把SRCLK改为SCK
#include <REGX52.H>
sbit RCK = P3^5; //RCLK
sbit SCK = P3^6; //SRCLK
sbit SER = P3^4; //SER
void main()
{
while(1)
{
}
}
再次编译,发现没有错误。
SER是一位数据,无论给什么数据,非0即1。
写一个移位的程序
void _74HC595_WriteByte(unsigned char Byte)
{
SER = Byte&0x80; //取出最高位
SCK = 1; //SRCLK上升沿,移位
SCK = 0; //置0,为下一次移位做准备
SER = Byte&0x40; //取出第二位
SCK = 1; //SRCLK上升沿,移位
SCK = 0; //置0,为下一次移位做准备
SER = Byte&0x20; //取出第三位
SCK = 1; //SRCLK上升沿,移位
SCK = 0; //置0,为下一次移位做准备
//... 直到第八位
}
一直写到第8位,太麻烦了,可以写一个for循环简化一下
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SER = Byte&(0x80>>i);
SCK = 1; //SRCLK上升沿,移位
SCK = 0; //置0,为下一次移位做准备
}
}
当8为全都到寄存器中,就可以给RCLK一个上升沿,把数据移过去了
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SER = Byte&(0x80>>i);
SCK = 1; //SRCLK上升沿,移位
SCK = 0; //置0,为下一次移位做准备
}
RCK = 1;
RCK = 0;
}
现在测试一下_74HC595_WriteByte
。由于新版普中2板只有一个LED模块,所以在函数体内加P2 = ~Byte;
来测试。
main函数如下:
void main()
{
SCK = 0;
RCK = 0;
while(1)
{
_74HC595_WriteByte(0xAA);
}
}
编译一下,可以看到D2, D4, D6, D8被点亮。这说明_74HC595_WriteByte
函数没问题。可以继续写点阵屏的代码。
可以参考数码管的代码。
把Delay.c和Delay.h复制到本项目。
定义一个函数
void MatrixLED_ShowColumn(unsigned char Column, Data)
{
_74HC595_WriteByte(Data);
if(Column==0){P0=~0x80;}
if(Column==1){P0=~0x40;}
//...
}
简化一下代码
void MatrixLED_ShowColumn(unsigned char Column, Data)
{
_74HC595_WriteByte(Data);
P0=~(0x80>>Column);
}
main函数:
void main()
{
SCK = 0;
RCK = 0;
MatrixLED_ShowColumn(0, 0xAA);
while(1)
{
}
}
编译一下,可以看到如图所示的现象
把0改为7就会亮最后一列。
如果是MatrixLED_ShowColumn(7, 0xF0);
就会亮最后一列,上面四个。
理论上来说,要实现点阵屏动态显示,应该是段选、位选、段选、位选……这样的操作。但是当次段选容易和上次位选混在一起,所以需要“延时+位清零”操作。即,段选、位选、延时+位清零、段选、位选、延时+位清零……
MatrixLED_ShowColumn()可以写为:
void MatrixLED_ShowColumn(unsigned char Column, Data)
{
_74HC595_WriteByte(Data);
P0=~(0x80>>Column);
Delay(1);//延时
P0 = 0xFF;//位清零
}
测试一下,可以看到MatrixLED_ShowColumn(7, 0xF0);
如果不放while(1)
循环里,LED闪一下就灭了。
现在在while(1)
循环里调用MatrixLED_ShowColumn()
void main()
{
SCK = 0;
RCK = 0;
while(1)
{
MatrixLED_ShowColumn(0, 0x80);
MatrixLED_ShowColumn(1, 0x40);
MatrixLED_ShowColumn(2, 0x20);
MatrixLED_ShowColumn(3, 0x10);
}
}
编译一下,可以看到如图所示现象
现在画个心形
void main()
{
SCK = 0;
RCK = 0;
while(1)
{
MatrixLED_ShowColumn(0, 0x38);
MatrixLED_ShowColumn(1, 0x44);
MatrixLED_ShowColumn(2, 0x42);
MatrixLED_ShowColumn(3, 0x21);
MatrixLED_ShowColumn(4, 0x21);
MatrixLED_ShowColumn(5, 0x42);
MatrixLED_ShowColumn(6, 0x44);
MatrixLED_ShowColumn(7, 0x38);
}
}
编译一下,可以看到:
四、模块化
新建MatrixLED.c和MatrixLED.h
在MatrixLED.c中写入:
#include <REGX52.H>
#include "Delay.h"
sbit RCK = P3^5; //RCLK
sbit SCK = P3^6; //SRCLK
sbit SER = P3^4; //SER
/**
* @brief 74HC595写入一个字节
* @param Byte 要写入的字节
* @retval 无
*/
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SER = Byte&(0x80>>i);
SCK = 1; //SRCLK上升沿,移位
SCK = 0; //置0,为下一次移位做准备
}
RCK = 1;
RCK = 0;
//P2 = ~Byte;
}
/**
* @brief 点阵屏初始化
* @param 无
* @retval 无
*/
void MatrixLED_Init()
{
SCK = 0;
RCK = 0;
}
/**
* @brief LED点阵屏显示一列数据
* @param Column 要选择的列,范围:0~7,0在最左边
* @param Data 所选列显示的数据,高位在上,1亮0灭
* @retval 无
*/
void MatrixLED_ShowColumn(unsigned char Column, Data)
{
_74HC595_WriteByte(Data);
P0=~(0x80>>Column);
Delay(1);//延时
P0 = 0xFF;//位清零
}
在MatrixLED.h中写入:
#ifndef __MATRIX_LED__H__
#define __MATRIX_LED__H__
void _74HC595_WriteByte(unsigned char Byte);
void MatrixLED_Init();
void MatrixLED_ShowColumn(unsigned char Column, Data);
#endif
此时,main.c中就简化为:
#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"
void main()
{
MatrixLED_Init();
while(1)
{
MatrixLED_ShowColumn(0, 0x38);
MatrixLED_ShowColumn(1, 0x44);
MatrixLED_ShowColumn(2, 0x42);
MatrixLED_ShowColumn(3, 0x21);
MatrixLED_ShowColumn(4, 0x21);
MatrixLED_ShowColumn(5, 0x42);
MatrixLED_ShowColumn(6, 0x44);
MatrixLED_ShowColumn(7, 0x38);
}
}
五、LED点阵屏显示动画
打开文字取模软件
新建宽度32、高度8的图像
然后放大格点
在格点中画个“Hello !”
在取模方式中点击C51格式,然后把生成的点阵位置复制
在main.c中定义一个unsigned char Animation[]={};
用来存储这些点阵位置。
unsigned char Animation[]={0xFF,0x08,0x08,0x08,0xFF,0x00,0x0E,0x15,0x15,0x0D,0x00,0x7F,0x01,0x00,0x7F,0x01,
0x00,0x0E,0x11,0x11,0x0E,0x00,0x7D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
main.c的程序为:
#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"
unsigned char Animation[]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0x08,0x08,0x08,0xFF,0x00,0x0E,0x15,
0x15,0x0D,0x00,0x7F,0x01,0x00,0x7F,0x01,
0x00,0x0E,0x11,0x11,0x0E,0x00,0x7D,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
void main()
{
unsigned char i;
unsigned char Offset=0;//偏移量
unsigned char Count=0;//计次
MatrixLED_Init();
while(1)
{
for(i=0;i<8;i++)
{
MatrixLED_ShowColumn(i, Animation[i+Offset]);
}
Count++;
if(Count>10)
{
Count = 0;
Offset++;
if(Offset>32)
{
Offset=0;
}
}
}
}
编译一下,可以看到“Hello !”字样滚动显示。