1. 显示原理
LED点阵屏与数码管类似,只是将数码管每一列的像素以8字型排列
与数码管一样,有共阴和共阳两种接法,不同接法对应电路结构不同
LED点阵需要逐行或逐列扫描,才能使所有LED同时显示
2. 74HC595电路图原理
看OE,是低电平有效,所以要弄一下J24这个接线帽
RCLK:Register Clock
SRCLR:Serial 串行清零端
SRCLK:串行时钟
SER:串行数据
74HC595是串行输入并行输出的移位寄存器,用3根线输入串行数据,8根线输出并行数据,多片级联可以输出16、24、32位等,常用于IO口扩展
数据通过SER一位一位地进来,上升沿移位SERCLK脉冲来一次就SER写一次数据,最后经上升沿锁存RCLK,将数据从左侧挪到右侧。
(弹匣装弹,霰弹枪出来的原理)
扩展多位的原理如下:
第一片板子SER里面的数据满了,就经过线送到下一片板子里,SERCLK和RCLK接在同一个位置。这样可以形成多片级联,形成更多位的输出。
3. 单片机
单片机输出端口是弱上拉的,给1的时候电流小,给0的时候VCC流回负极电流更大,如果想要将点阵直接接在单片机的端口上是不可行的,需要加一个三极管,三极管作为放大电路使用。
4. 74HC595的使用
江科大使用的单片机上74HC595模块是有接LED灯,以观察输出情况的,我买的普中板子上没有,所以只是按照看视频的理解写了相应的函数。
#include <REGX52.H>
// 重新命名,会更清晰一些,要不然容易不清楚每个口的具体含义
// P3^X:这不是异或的意思,这样的形式表示选中P3的第5位
sbit RClK = P3^5; // 上升沿锁存,置1时将SER的内容全部写入输出口
sbit SRCLK = P3^6; // 上升沿移位,置1时写入SER的内容
sbit SER = P3^4;
void _74HC595_WriteByte(unsigned char byteData)
{
unsigned char i = 0;
for (i = 0; i < 8; i++)
{
SER = byteData & (0x80 >> i); // 通过这种方式取出第i位
// byteData & 0x80相当于选出最高位的数据,结果要么是1000 0000,要么是0000 0000
// 因为SER的类型是sbit,所以要么为1要么为0
// 如果结果1000 0000,SER就会被置为1
// 如果结果0000 0000,SER就会被置为0
// SRCLK原本为0,置1给一个电平写入,然后置0
SRCLK = 1;
SRCLK = 0;
}
// RClK原本为0,置1给一个电平统一转移数字,然后置0
RClK = 1;
RClK = 0;
}
void main()
{
SRCLK = 0; // 初始置0,后面才更容易给电平
RClK = 0; // 初始置0,后面才更容易给电平
while(1)
{
}
}
5. 点阵屏的显示
在4中函数的基础上,选中列后再对行进行选中,就可以实现点阵屏显示
#include <REGX52.H>
#include "Delay.h"
// 重新命名,会更清晰一些,要不然容易不清楚每个口的具体含义
// P3^X:这不是异或的意思,这样的形式表示选中P3的第5位
sbit RClK = P3^5; // 上升沿锁存,置1时将SER的内容全部写入输出口
sbit SRCLK = P3^6; // 上升沿移位,置1时写入SER的内容
sbit SER = P3^4;
void _74HC595_WriteByte(unsigned char byteData)
{
unsigned char i = 0;
for (i = 0; i < 8; i++)
{
SER = byteData & (0x80 >> i); // 通过这种方式取出第i位
// byteData & 0x80相当于选出最高位的数据,结果要么是1000 0000,要么是0000 0000
// 因为SER的类型是sbit,所以要么为1要么为0
// 如果结果1000 0000,SER就会被置为1
// 如果结果0000 0000,SER就会被置为0
// SRCLK原本为0,置1给一个电平写入,然后置0
SRCLK = 1;
SRCLK = 0;
}
// RClK原本为0,置1给一个电平统一转移数字,然后置0
RClK = 1;
RClK = 0;
}
void MatrixLED_ShowColumn(unsigned char column, byteData)
{
_74HC595_WriteByte(byteData); // 选中列
P0 = ~(0x80) >> column; // 选中指定行,给低电平点灯,column表示点亮第几行,做一个右移
// 避免显示下一个的时候出现残影,Delay后置灭
Delay(1);
P0 = 0xFF;
}
void main()
{
SRCLK = 0; // 初始置0,后面才更容易给电平
RClK = 0; // 初始置0,后面才更容易给电平
MatrixLED_ShowColumn(7,0xAA);
while(1)
{
}
}
对于为什么要Delay和置灭,是因为这通常是一个:
段选→位选→段选→位选→…的过程
这会导致一个问题,在执行第三个段选的时候,上一次的位选还没改变,从而导致上一次的位选和下一次的段选结合,呈现错误的结果/残影,所以正确的是:
段选→位选→延时→位清零→段选→位选→…的过程
6. 矩阵LED屏显示自定义图案
有了上面的基础,显示笑脸就很简单了,用EXCEL画出需要亮灯的部分:
第一列:0011 1100 → 3C
第二列:0100 0010 → 42
第三列:1010 1001 → A9
第四列:1000 0101 → 85
第五列:1000 0101 → 85
第六列:1010 1001 → A9
第七列:0100 0010 → 42
第八列:0011 1100 → 3C
主函数修改为:
void main()
{
SRCLK = 0; // 初始置0,后面才更容易给电平
RClK = 0; // 初始置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);
}
}
效果如下(长曝光):
类似的如果是一个爱心:
第一列:0001 1000 → 18
第二列:0010 0100 → 24
第三列:0100 0010 → 42
第四列:0010 0001 → 21
第五列:0010 0001 → 21
第六列:0100 0010 → 42
第七列:0010 0100 → 24
第八列:0001 1000 → 18
主函数修改为:
void main()
{
SRCLK = 0; // 初始置0,后面才更容易给电平
RClK = 0; // 初始置0,后面才更容易给电平
while(1)
{
MatrixLED_ShowColumn(0, 0x18);
MatrixLED_ShowColumn(1, 0x24);
MatrixLED_ShowColumn(2, 0x42);
MatrixLED_ShowColumn(3, 0x21);
MatrixLED_ShowColumn(4, 0x21);
MatrixLED_ShowColumn(5, 0x42);
MatrixLED_ShowColumn(6, 0x24);
MatrixLED_ShowColumn(7, 0x18);
}
}
效果如下(长曝光):
7. 矩阵LED屏显示动画
我们的LED一共是8列,显示动画的原理实际上是将每列要显示内容组成一个数组,然后每次显示其中8列,例如第一次显示数组的0-7,第二次显示数组的1-8,以此类推。
具体显示的字符动画可以利用字模提取软件获得对应的结果,使用步骤如下:
第一步:新建图像,高度为8(开发板上的高度,宽度选择对应字符长度)
第二步:模拟动画里放大格点
第三步:绘制格点:
第四步:生成点阵
接下来就可以写代码,实际上就是用循环来遍历上面的这些内容:
#include <REGX52.H>
#include "MatrixLED.h"
#include "Delay.h"
unsigned char Animation[] = {
0x00,0x00,0x00,0x00,0x00,0xFF,0x81,0x42,0x3C,0x00,0x0C,0x12,0x12,0x0E,0x01,0x00,
0xFF,0x81,0x42,0x3C,0x00,0x0C,0x12,0x12,0x0E,0x01,0x00,0xFD,0x00,0x00,0x00,0x00,
}; // 字符提取软件生成
void main()
{
unsigned int i = 0, offset = 0, count = 0;
Init_Matrix_LED();
while(1)
{
for (i = 0; i < 8; i++) // for循环,8位循环显示
{
MatrixLED_ShowColumn(i, Animation[i+offset]); // offset表示数组中的便宜
}
// count这里实际上起到一个控制速度的作用,大于号后面的数字越大则动的越慢
count++;
if (count > 10)
{
count = 0;
offset++; // 增加偏移量
if (offset > 24) offset = 0; // 偏移量到临界时需要重置,避免乱码
}
}
}
这里控制速度不能用Delay,用Delay的话点阵屏上的结果会消失,这应该是我们的点亮函数会置0导致的。
效果如下:
附上MatrixLED的内容:
MatrixLED.c:
#include <REGX52.H>
#include "Delay.h"
// 重新命名,会更清晰一些,要不然容易不清楚每个口的具体含义
// P3^X:这不是异或的意思,这样的形式表示选中P3的第5位
sbit RClK = P3^5; // 上升沿锁存,置1时将SER的内容全部写入输出口
sbit SRCLK = P3^6; // 上升沿移位,置1时写入SER的内容
sbit SER = P3^4;
#define MATRIX_LED_PORT P0
/**
* @brief 矩阵LED初始化
* @param 无
* @retval 无
*/
void Init_Matrix_LED()
{
SRCLK = 0; // 初始置0,后面才更容易给电平
RClK = 0; // 初始置0,后面才更容易给电平
}
/**
* @brief 74HC595 写入一个字节
* @param 要写入的字节
* @retval 无
*/
void _74HC595_WriteByte(unsigned char byteData)
{
unsigned char i = 0;
for (i = 0; i < 8; i++)
{
SER = byteData & (0x80 >> i); // 通过这种方式取出第i位
// byteData & 0x80相当于选出最高位的数据,结果要么是1000 0000,要么是0000 0000
// 因为SER的类型是sbit,所以要么为1要么为0
// 如果结果1000 0000,SER就会被置为1
// 如果结果0000 0000,SER就会被置为0
// SRCLK原本为0,置1给一个电平写入,然后置0
SRCLK = 1;
SRCLK = 0;
}
// RClK原本为0,置1给一个电平统一转移数字,然后置0
RClK = 1;
RClK = 0;
}
/**
* @brief LED点阵屏显示一列数据
* @param
* @retval
*/
void MatrixLED_ShowColumn(unsigned char column, byteData)
{
_74HC595_WriteByte(byteData); // 选中列
MATRIX_LED_PORT = ~(0x80) >> column; // 选中指定行,给低电平点灯,column表示点亮第几行,做一个右移
// 避免显示下一个的时候出现残影,Delay后置灭
Delay(1);
MATRIX_LED_PORT = 0xFF;
}
MatrixLED.h:
#ifndef __MATRIXLED_H__
#define __MATRIXLED_H__
void Init_Matrix_LED();
void _74HC595_WriteByte(unsigned char byteData);
void MatrixLED_ShowColumn(unsigned char column, byteData);
#endif
Delay.c:
#include <INTRINS.H>
void Delay(unsigned int ms) //@11.0592MHz
{
unsigned char i, j;
while (ms)
{
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
ms--;
}
}
Delay.h:
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay(unsigned int ms);
#endif