文章目录
一、LED点阵屏介绍
LED点阵屏由若干个独立的LED组成,LED以矩阵的形式排列,以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种公共场合,如汽车报站器、广告屏以及公告牌等
LED点阵屏分类:
按颜色:单色、双色、全彩
按像素:8*8、16*16等(大规模的LED点阵通常由很多个小点阵拼接而成)
(本开发板是单色8*8的点阵屏)
二、LED点阵屏工作原理
LED点阵屏的结构类似于数码管,只不过是数码管把每一列的像素以“8”字型排列而已。原理图如下
每一行的阳极连在一起,每一列的阴极连在一起
LED点阵屏需要进行逐行或逐列扫描(扫描的速度很快),才能使所有LED同时显示
例如,要想显示第二行第二列的LED灯,只需将DPg置1,其余行置0,P06置0,其余列置1,即可
尽管使用8*8的矩阵来控制64个LED等,但16个IO口还是比较多的,并且也不能直接使用16个IO口,因为本单片机IO口输出是弱上拉的,输出高电平的电流较小,输出低电平的电流较大(正常),所以如果直接用16个IO口的话,LED点阵屏的每一行的输出的电流较小,会是的灯光较暗。
于是本开发板使用了74HC595模块来控制A1-A8这8行的IO口,74HC595模块原理图如下,使用P34、P35、P36三个IO口来控制8个IO口的输出。J24模块在LED点阵屏的左上方。
三、74HC595
74HC595是串行输入并行输出的移位寄存器,可用3根线输入串行数据,8根线输出并行数据,多片级联后,可输出16位、24位、32位等,常用于IO口扩展。
OE ‾ \overline{\text{OE}} OE:输出使能,低电平有效,接GND,即使用跳线帽将J24的OE和GND连起来(开发板默认用跳线帽连接OE和GCC,需要将跳线帽连接左边两个口)
RCLK:寄存器时钟
SRCLR ‾ \overline{\text{SRCLR}} SRCLR:串行清零,接VCC,表示不清空数据
SRCLK:串行时钟,用于输入串行数据
SER:串行数据输入端
QH’:用于多片级联,接到下一个74HC595的SER口
74HC595工作原理:
74HC595工作原理简图如下:
该模块可用于数据串行输入,并行输出
左边是个移位寄存器,SER输入一位数据,SERCLK在上升沿到来时,将移位寄存器的数据向下移动一位,之后SER再输入一位数据,在SERCLK时钟的上升沿时移位,数据填满移位寄存器后,在RCLK的上升沿到来时,将该寄存器的数据复制到右边的数据缓冲寄存器中,即为输出的数据。同时数据也可以通过QH‘输出到下一个74HC595的SER口作为串行数据输入。
开发板引脚对应关系:
LED点阵屏的列直接接在IO口上,通过P0口直接输入,行通过74HC595来进行数据输入
四、实验1
LED点阵屏显示静态图形
补充:C51的sfr、sbit
•sfr(special function register):特殊功能寄存器声明
例:sfr P0 = 0x80;
声明P0口寄存器,物理地址为0x80
•sbit(special bit):特殊位声明
例:sbit P0_1 = 0x81; 或 sbit P0_1 = P0^1;
声明P0寄存器的第1位
•可位寻址/不可位寻址:在单片机系统中,操作任意寄存器或者某一位的数据时,必须给出其物理地址,又因为一个寄存器里有8位,所以位的数量是寄存器数量的8倍,单片机无法对所有位进行编码,故每8个寄存器中,只有一个是可以位寻址的。对不可位寻址的寄存器,若要只操作其中一位而不影响其它位时,可用“&=”、“|=”、“^=”的方法进行位操作
1、位声明
根据原理图,将IO口进行位声明,方面之后使用
sbit SER = P3^4;
sbit SERCLK = P3^6;
sbit RCK = P3^5; //RCLK
2、74HC595写入字节函数
/**
* @brief 74HC595写入一个字节
* @param byte 要写入的字节
* @retval 无
*/
void _74HC595_WriteByte(unsigned char byte)
{
unsigned char i;
for(i = 0;i < 8;i++)
{
//以byte&0x80为例,byte和0x80与运算后,首位不变,其余位变为0;若最高位为1,则与运算的结果不为0,
//即SER为1(SER是一个位,赋值时非0即为1),若最高位为0,则与运算的结果为0,即SER为0
//这样就可以获取byte的最高位
//其余位的获取,同理
SER = byte&(0x80 >> i);
SERCLK = 1;//一开始SERCLK为0,这里置为1,就产生了上升沿
SERCLK = 0;//为下一次移位做准备
}
RCK = 1;
RCK = 0;
}
3、测试写入字节函数
#include <REGX52.H>
sbit SER = P3^4;
sbit SERCLK = P3^6;
sbit RCK = P3^5; //RCLK
/**
* @brief 74HC595写入一个字节
* @param byte 要写入的字节
* @retval 无
*/
void _74HC595_WriteByte(unsigned char byte)
{
unsigned char i;
for(i = 0;i < 8;i++)
{
//以byte&0x80为例,byte和0x80与运算后,首位不变,其余位变为0;若最高位为1,则与运算的结果不为0,
//即SER为1(SER是一个位,赋值时非0即为1),若最高位为0,则与运算的结果为0,即SER为0
//这样就可以获取byte的最高位
//其余位的获取,同理
SER = byte&(0x80 >> i);
SERCLK = 1;//一开始SERCLK为0,这里置为1,就产生了上升沿
SERCLK = 0;//为下一次移位做准备
}
RCK = 1;
RCK = 0;
}
void main()
{
//上电时为高电平
SERCLK = 0;
RCK = 0;
_74HC595_WriteByte(0xF0);
while(1)
{
}
}
注意:这个板子没有江科大视频中的LED灯,所以无法验证函数的正确性
4、显示数据函数
与数码管类似,以列为单位进行显示
同时,为了消除残影,需要在函数的最后进行P0口的清零
/**
* @brief LED点阵屏显示一列数据
* @param column 要选择的列,范围:0~7,0在最左边
* @param myData 选择列显示的数据,高位在上,1为亮,0为灭
* @retval 无
*/
void MatrixLED_ShowColumn(unsigned char column, myData)
{
_74HC595_WriteByte(myData);
//选中某一列
P0 = ~(0x80>>column);//列,低电平有效,故要取反
Delayms(1);
MATRIX_LED_PORT=0xFF;//位清零
}
5、LED点阵屏显示笑脸(实验最终现象)
通过控制每一列的灯的亮灭来显示整个图像
#include <REGX52.H>
#include "Delay.h"
sbit SER = P3^4;
sbit SERCLK = P3^6;
sbit RCK = P3^5; //RCLK
#define MATRIX_LED_PORT P0
/**
* @brief 74HC595写入一个字节
* @param Byte 要写入的字节
* @retval 无
*/
void _74HC595_WriteByte(unsigned char byte)
{
unsigned char i;
for(i = 0;i < 8;i++)
{
//以byte&0x80为例,byte和0x80与运算后,首位不变,其余位变为0;若最高位为1,则与运算的结果不为0,
//即SER为1(SER是一个位,赋值时非0即为1),若最高位为0,则与运算的结果为0,即SER为0
//这样就可以获取byte的最高位
//其余位的获取,同理
SER = byte&(0x80 >> i);
SERCLK = 1;//一开始SERCLK为0,这里置为1,就产生了上升沿
SERCLK = 0;//为下一次移位做准备
}
RCK = 1;
RCK = 0;
}
/**
* @brief LED点阵屏显示一列数据
* @param column 要选择的列,范围:0~7,0在最左边
* @param myData 选择列显示的数据,高位在上,1为亮,0为灭
* @retval 无
*/
void MatrixLED_ShowColumn(unsigned char column, myData)
{
_74HC595_WriteByte(myData);
MATRIX_LED_PORT = ~(0x80>>column);//列,低电平有效
Delayms(1);
MATRIX_LED_PORT=0xFF;//位清零
}
void main()
{
//上电时为高电平
SERCLK = 0;
RCK = 0;
while(1)
{
MatrixLED_ShowColumn(0,0x3C);
MatrixLED_ShowColumn(1,0x42);
MatrixLED_ShowColumn(2,0xA9);
MatrixLED_ShowColumn(3,0x85);
MatrixLED_ShowColumn(4,0x85);
MatrixLED_ShowColumn(5,0xA9);
MatrixLED_ShowColumn(6,0x42);
MatrixLED_ShowColumn(7,0x3C);
}
}
四、实验2
LED点阵屏显示动画
1、点阵屏模块化
将实验1的代码进行模块化
MatrixLED.h
#ifndef _MATRIXLED_H__
#define _MATRIXLED_H__
void _74HC595_WriteByte(unsigned char byte);
void MatrixLED_ShowColumn(unsigned char column, myData);
void MatrixLED_Init();
#endif
MatrixLED.c
#include <REGX52.H>
#include "Delay.h"
sbit SER = P3^4;
sbit SERCLK = P3^6;
sbit RCK = P3^5; //RCLK
#define MATRIX_LED_PORT P0
/**
* @brief 74HC595写入一个字节
* @param Byte 要写入的字节
* @retval 无
*/
void _74HC595_WriteByte(unsigned char byte)
{
unsigned char i;
for(i = 0;i < 8;i++)
{
//以byte&0x80为例,byte和0x80与运算后,首位不变,其余位变为0;若最高位为1,则与运算的结果不为0,
//即SER为1(SER是一个位,赋值时非0即为1),若最高位为0,则与运算的结果为0,即SER为0
//这样就可以获取byte的最高位
//其余位的获取,同理
SER = byte&(0x80 >> i);
SERCLK = 1;//一开始SERCLK为0,这里置为1,就产生了上升沿
SERCLK = 0;//为下一次移位做准备
}
RCK = 1;
RCK = 0;
}
/**
* @brief LED点阵屏显示一列数据
* @param column 要选择的列,范围:0~7,0在最左边
* @param myData 选择列显示的数据,高位在上,1为亮,0为灭
* @retval 无
*/
void MatrixLED_ShowColumn(unsigned char column, myData)
{
_74HC595_WriteByte(myData);
MATRIX_LED_PORT = ~(0x80>>column);//列,低电平有效
Delayms(1);
MATRIX_LED_PORT=0xFF;//位清零
}
/**
* @brief LED点阵屏初始化
* @param 无
* @retval 无
*/
void MatrixLED_Init()
{
//上电时为高电平
SERCLK = 0;
RCK = 0;
}
2、利用文字取模软件生成图像数据
点击“基本操作”,再点击“新建图像”,高度选8,宽度随意,这里选32
之后点击“参数设置”,再点击“其他选项”,在这里选择纵向取模,不选择字节倒序,其余不变
点击“模拟动画”,再点击“放大格点”,将图像进行放大
描绘自己想要的图像
点击“取模方式“,选择C51格式,将生成的数据保存
3、保存图像数据
将数据保存在数组中,数组的每一个元素表示一列LED的数据
//动画数据
unsigned char code Animation[]={
0xFF,0x08,0x08,0x08,0xFF,0x00,0x00,0x1C,0x2A,0x2A,0x1A,0x00,0x00,0x7F,0x01,0x01,
0x01,0x00,0x00,0x7F,0x01,0x01,0x00,0x00,0x1C,0x22,0x22,0x1C,0x00,0x00,0x7D,0x00,
};
4、主函数
#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"
//动画数据
unsigned char Animation[]={
0xFF,0x08,0x08,0x08,0xFF,0x00,0x00,0x1C,0x2A,0x2A,0x1A,0x00,0x00,0x7F,0x01,0x01,
0x01,0x00,0x00,0x7F,0x01,0x01,0x00,0x00,0x1C,0x22,0x22,0x1C,0x00,0x00,0x7D,0x00,
};
void main()
{
//Offset:偏移量 Count:扫描次数
unsigned char i,Offset=0,Count=0;
MatrixLED_Init();
while(1)
{
for(i=0;i<8;i++) //循环8次,显示8列数据
{
MatrixLED_ShowColumn(i,Animation[i+Offset]);
}
Count++;
if(Count>10) //扫描10次时,相当于延时
{
Count=0;
Offset++; //显示下一列数据
if(Offset>24) //32-8=24
{
Offset=0;
}
}
}
}
此时,LED点阵屏流动显示完字幕后会立刻从头开始,为了显示出流动的效果,在数组的前面和后面添加0x00
#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,0x00,0x1C,
0x2A,0x2A,0x1A,0x00,0x00,0x7F,0x01,0x01,
0x01,0x00,0x00,0x7F,0x01,0x01,0x00,0x00,
0x1C,0x22,0x22,0x1C,0x00,0x00,0x7D,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
void main()
{
//Offset:偏移量 Count:扫描次数
unsigned char i,Offset=0,Count=0;
MatrixLED_Init();
while(1)
{
for(i=0;i<8;i++) //循环8次,显示8列数据
{
MatrixLED_ShowColumn(i,Animation[i+Offset]);
}
Count++;
if(Count>10) //扫描10次时,相当于延时
{
Count=0;
Offset++; //显示下一列数据
if(Offset>40)//48-8=40
{
Offset=0;
}
}
}
}
补充:闪屏动画(逐帧)
逐帧显示HELLO
#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"
//动画数据
unsigned char code Animation[]={
0x00,0xFF,0x08,0x08,0x08,0x08,0xFF,0x00,
0x00,0xFF,0x89,0x89,0x89,0x89,0x00,0x00,
0x00,0xFF,0x01,0x02,0x02,0x04,0x00,0x00,
0x00,0xFF,0x01,0x02,0x02,0x04,0x00,0x00,
0x00,0x3E,0x41,0x41,0x41,0x41,0x3E,0x00,
};
void main()
{
//Offset:偏移量 Count:扫描次数
unsigned char i,Offset=0,Count=0;
MatrixLED_Init();
while(1)
{
for(i=0;i<8;i++) //循环8次,显示8列数据
{
MatrixLED_ShowColumn(i,Animation[i+Offset]);
}
Count++;
if(Count>40) //扫描10次时,相当于延时
{
Count=0;
Offset+=8; //显示下一列数据
if(Offset>32)
{
Offset=0;
}
}
}
}
补充:Animation数组是存储在RAM中,当要显示的图像很多时,数据量会很大,RAM可能会不够,所以可以将数据保存在Flash里面,在char后面添加一个code就行。但是定义之后便不能更改(ROM的特性)。
unsigned char code Animation[]={};
更多51单片机笔记见主页