1.LED点阵简介
1.1、什么是LED点阵
内部结构原理图。点阵的优势就是8+8个IO口独立控制8*8个LED亮灭。
1.2、原理图分析
(1)单片机端口直接驱动。要驱动88的点阵需要2个IO端口(16个IO口)、要驱动1616的点阵需要4个IO端口(32个IO口)。
(2)使用串转并移位锁存器驱动。要驱动16*16点阵只需要4个74HC595+3个IO口即可(数据口、SLCK、RCLK)
2.原理图分析
2.1、POS1-16和NEG1-16分别接移位锁存器并行输出端
POS就是Positive正极,NEG就是Negetive负极。
2.1、74HC595的接法分析
(1)QA-QH 8路并行输出接到点阵的一个接口
(2)QH'串行输出口接下一个74HC595的串行输入SER(串联顺序按照ABCD)
(3)SER串行输入接:第一个595的SER通过跳线帽JP595接P3.4,后面的每一个SER接前一个的QH'。这样就构成了ABCD4个595芯片依次串联。所以将来编程时整个4个74HC595的串行数据都是从P3.4出来的。
(4)SCLK(SRCLK)接P3.6
(5)RCLK接P3.5
总结:
①SCLK和RCLK是一样的接法,都是接在P3.5和P3.6上
②总共涉及到的IO口有3个:P3.4、P3.5、P3.6
③外部接线重点:2个8pin杜邦线+1个跳线帽
3.LED点阵编程实践1
3.1、74HC595的时序分析
(1)芯片与芯片之间的通信,都是按照一定的时序进行的。
(2)时序就是芯片与芯片之间的通信引脚上电平变化以时间轴为参考的变化顺序。
(3)时序是通信双方事先定义好的,通信的发送方必须按照时序来发送有意义的信息,通信的接收方按照时序去接收解析发送方发来的电平变化信息,然后就知道发送方要给我发送什么东西了。
(4)我们编程时:发送方是单片机,接收方是74HC595。因为595芯片本身是不能编程的,他的时序芯片出厂时已经设定好了。因此我们单片机必须迁就595芯片,按照595芯片的时序来给它发信息。
(5)所以我们要先搞清楚74HC595的时序规则。595的芯片手册上就有它的时序描述(时序图),参考描述就可以明白595芯片的时序规则,然后将其用编程语言表述出来就是程序了。
(6)74HC595的时序关键是:SCLK和RCLK。SCLK是移位时钟,595芯片内部在每个SCLK的上升沿会对SER引脚进行一次采样输入,就向595内部输入了1位,如此循环8次就输入了8位二进制。RCLK是锁存时钟,QA-QH的8位并行输出信号在RCLK的上升沿进行一次锁存更新。
(7)理解74HC595芯片的通信时序关键,其实就是:SER进行数据的串行输入,SCLK提供移位时钟,RCLK提供锁存时钟。
3.2、sbit定义位变量
(1)之前编程都是直接操作一个IO端口,可以用端口名(P0、P1)来整体操作一个IO端口中的8个引脚。但是这种方法不能操控单独1个IO口。
(2)今天编程需要单独操作1个IO引脚,比如要操作P3.4,但是直接写P3.4的话C语言是不认识的,而必须使用sbit关键字来定义一个引脚。sibt SER = P3^4;
4.LED点阵编程实践2
4.1、编写移位寄存器传送函数SendData
(1) SER = d1 >> 7; SCLK = 0;SCLK = 1;
这个代码的意思可以形象的这样理解,SCLK是一个货梯,SER是货梯上的篮子,SCLK为0的时候就是就位状态,这时候你要把数据放到SER里,当SCLK为1的时候就启动货梯,把数据运上去给QA-QH。
(2) RCLK = 0; RCLK = 1;
没有运行这两句锁存语句时,点阵也会亮,但是不可保证电平时稳定一直不变的。当锁存后才可以保证电平是稳定的。
4.2、全屏点亮测试
(1)编写硬件控制代码,时序理解是关键。只要时序理解正确的,并且代码按照时序的要求去写,就没问题。
(2)时序操作部分的代码只要写好了并且是正确的,下来这一块就没问题,很简单,因为它是死板的不变的。
#include
sbit SER = P3^4;
sbit RCLK = P3^5;
sbit SCLK = P3^6;
void main(void)
{
unsigned char i = 0;
unsigned char d1, d2, d3, d4; // 要给4个595并行输出端输出的值
SCLK = 0;
RCLK = 0;
d1 = 0x0; // d1和d2负极
d2 = 0x0;
d3 = 0xff; // d3和d4负极
d4 = 0xff;
for (i =0; i<8; i++)
{
SER = d1 >> 7; // 将d1的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 电位一低一高,2步制造一个SCLK的上升沿
d1 = d1 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经在8个SLCK上升沿把d1的8位依次全部发出去了,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
for (i =0; i<8; i++)
{
SER = d2 >> 7; // 将d2的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 2步制造一个SCLK的上升沿
d2 = d2 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经把d1和d2发出去了,并且d1已经被d2挤到第2个595芯片里面,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
for (i =0; i<8; i++)
{
SER = d3 >> 7; // 将d3的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 2步制造一个SCLK的上升沿
d3 = d3 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经把d1和d2和d3发出去了,并且d1已经被d2和d3挤到第3个595芯片里面,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
for (i =0; i<8; i++)
{
SER = d4 >> 7; // 将d4的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 2步制造一个SCLK的上升沿
d4 = d4 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经把d1和d2和d3和d4发出去了,
// 并且d1已经被d2和d3和d4挤到第4个595芯片里面,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
/*
至此,4个字节的数据d1、d2、d3、d4已经顺着74HC595的SER→QH'的串行输出,
串行输出路线,已经爬满了4个74HC595(最先送出去的到了最后一个595中)
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
然后要进行一次锁存,4个595芯片同时在进行锁存,各自锁存住了自己的数据。
*/
RCLK = 0; // 电尾一低一高就完成锁存了
RCLK = 1;
// 这两句之后595就完成了锁存,d1-d4就会影响4个595芯片的并行输出端,
// 进而会影响点阵中LED的正负极的值,然后LED就会亮或者灭
// 按照我们写的程序,d1和d2是负极,d3和d4是正极
}
5.LED点阵编程实践3
5.1、宏定义的引入和uchar、u8
#include
sbit SER = P3^4;
sbit RCLK = P3^5;
sbit SCLK = P3^6;
#define uchar unsigned char
// 函数声明
void SendData(uchar d1, uchar d2, uchar d3, uchar d4)
void main(void)
{
// d1、d2对应负极,d3、d4对应正极
// d1、d3对应9-16,d2、d4对应1-8
// d1-d4都是最低bit位表示编号小的数字,比如d4的bit0对应1
// d3和d4是正极,规律是1亮0灭;d1和d2是负极,规律是1灭0亮
// SendData(0x00, 0x00, 0xff, 0xff); // 全亮
// SendData(0x00, 0x00, 0x00, 0xff); // 亮上半屏
// SendData(0x00, 0x00, 0xff, 0x00); // 亮下半屏
// SendData(0x00, 0x00, 0x00, 0x01); // 亮上半屏第一行
// SendData(0x00, 0x00, 0x80, 0x00); // 亮下半屏最后一行
// SendData(0xff, 0xfe, 0xff, 0xff); // 亮最左一列
// SendData(0x7f, 0xff, 0xff, 0xff); // 亮最右一列
// SendData(0x7f, 0xff, 0x00, 0x01); // 亮右上角一颗
// SendData(0x7f, 0xff, 0x80, 0x00); // 亮右下角一颗
// SendData(0x7f, 0xfe, 0x80, 0x01); // 亮角上四颗
}
void SendData(uchar d1, uchar d2, uchar d3, uchar d4)
{
unsigned char i = 0;
SCLK = 0;
RCLK = 0;
for (i =0; i<8; i++)
{
SER = d1 >> 7; // 将d1的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 电位一低一高,2步制造一个SCLK的上升沿
d1 = d1 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经在8个SLCK上升沿把d1的8位依次全部发出去了,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
for (i =0; i<8; i++)
{
SER = d2 >> 7; // 将d2的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 2步制造一个SCLK的上升沿
d2 = d2 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经把d1和d2发出去了,并且d1已经被d2挤到第2个595芯片里面,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
for (i =0; i<8; i++)
{
SER = d3 >> 7; // 将d3的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 2步制造一个SCLK的上升沿
d3 = d3 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经把d1和d2和d3发出去了,并且d1已经被d2和d3挤到第3个595芯片里面,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
for (i =0; i<8; i++)
{
SER = d4 >> 7; // 将d4的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 2步制造一个SCLK的上升沿
d4 = d4 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经把d1和d2和d3和d4发出去了,
// 并且d1已经被d2和d3和d4挤到第4个595芯片里面,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
/*
至此,4个字节的数据d1、d2、d3、d4已经顺着74HC595的SER→QH'的串行输出,
串行输出路线,已经爬满了4个74HC595(最先送出去的到了最后一个595中)
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
然后要进行一次锁存,4个595芯片同时在进行锁存,各自锁存住了自己的数据。
*/
RCLK = 0; // 电尾一低一高就完成锁存了
RCLK = 1;
// 这两句之后595就完成了锁存,d1-d4就会影响4个595芯片的并行输出端,
// 进而会影响点阵中LED的正负极的值,然后LED就会亮或者灭
// 按照我们写的程序,d1和d2是负极,d3和d4是正极
}
6.字模介绍
6.1、何为字模
(1)如何记录组成字的LED点阵亮灭信息(1616点阵一共有256点,显示一个特定的字需要其中有些亮而不亮,如何记录哪些点亮哪些点不亮?用字模)
字模如何工作?
256个点用256个二进制位表示,1表示这个点亮,0表示不亮。256个点就是256个二进制位,也就是256/8=32个字节。所以一个大小为1616的字的字模是32个字节大小。所以字模的表现形式就是32个unsigned char型数据。
(2)字模如何获取?
一般都是用专门的字模提取软件去提取的。这种软件的作用就是给一个字就能自动得到这个字对应的32个字节的字模编码。
(3)字模的结果不是唯一的,和你提取字模的方式有关的。(横向纵向、从上到下之类区分)提取字模时是没有标准的,怎么做都是对的或者都是错的,关键是你提取字模的方式和你用来在点阵上显示这个字模的函数必须对应。
6.2、字模提取软件的使用
(1)使用方式:第一步先选择字形(实际开发板上点阵多大就选择多大),第二步再选择合适的字体、字号等,第三步选择编码方式和取模方向,第四步直接将得到的数组复制走。
6.3、字模的手工分析和验证
(1)手工对比字模内容和屏幕显示,从而确认取模方式
7.横向取模的显示函数1
7.1、工程建立
7.2、先显示一行
7.3、多显示2行去探索规律
(1)规律1:d1和d2用字模来填充,填充时要取反。因为字模软件1为亮0为灭,d1和d2对应负极,1为灭0为亮。
(2)规律2:d3和d4来选择哪一行被点亮,而d1和d2选择这行中哪一列被点亮。
(3)规律3:SendData依次送16个LED的亮灭信息(2字节),所以必须调用256/16=16次SendData函数,才能把整个点阵全部点亮完毕。
(4)规律4:每次调用SendData时,d1-d4变化都是有规律的,因此有希望通过循环来调用SendData而无需手工调用16次。
#include
sbit SER = P3^4;
sbit RCLK = P3^5;
sbit SCLK = P3^6;
#define uchar unsigned char
// 函数声明
void SendData(uchar d1, uchar d2, uchar d3, uchar d4)
unsigned char zhu[32] =
{
128, 0, 136, 0, 136, 0, 248, 31,
132, 0, 130, 0, 128, 0, 255, 127,
192, 1, 160, 2, 144, 4, 136, 8,
132, 16, 131, 96, 128, 0, 128, 0
};
void main(void)
{
SendData(~0, ~128, 0x00, 0x01); // 显示第1行
SendData(~0, ~136, 0x00, 0x02); // 显示第2行
SendData(~0, ~136, 0x00, 0x04); // 显示第3行
SendData(~31, ~248, 0x00, 0x08); // 显示第4行
SendData(~0, ~132, 0x00, 0x10); // 显示第5行
SendData(~0, ~130, 0x00, 0x20); // 显示第6行
SendData(~0, ~128, 0x00, 0x40); // 显示第7行
SendData(~127, ~255, 0x00, 0x80); // 显示第8行
}
void SendData(uchar d1, uchar d2, uchar d3, uchar d4)
{
unsigned char i = 0;
SCLK = 0;
RCLK = 0;
for (i =0; i<8; i++)
{
SER = d1 >> 7; // 将d1的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 电位一低一高,2步制造一个SCLK的上升沿
d1 = d1 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经在8个SLCK上升沿把d1的8位依次全部发出去了,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
for (i =0; i<8; i++)
{
SER = d2 >> 7; // 将d2的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 2步制造一个SCLK的上升沿
d2 = d2 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经把d1和d2发出去了,并且d1已经被d2挤到第2个595芯片里面,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
for (i =0; i<8; i++)
{
SER = d3 >> 7; // 将d3的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 2步制造一个SCLK的上升沿
d3 = d3 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经把d1和d2和d3发出去了,并且d1已经被d2和d3挤到第3个595芯片里面,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
for (i =0; i<8; i++)
{
SER = d4 >> 7; // 将d4的最高bit取出来给SER
SCLK = 0;
SCLK = 1; // 2步制造一个SCLK的上升沿
d4 = d4 << 1; // 最高位丢掉,第二高位排到最高位给第一条语句执行取出
}
// 至此已经把d1和d2和d3和d4发出去了,
// 并且d1已经被d2和d3和d4挤到第4个595芯片里面,
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
/*
至此,4个字节的数据d1、d2、d3、d4已经顺着74HC595的SER→QH'的串行输出,
串行输出路线,已经爬满了4个74HC595(最先送出去的到了最后一个595中)
// 但是还没有进行锁存,所以QA-QH有东西但是不能保证一直不变
然后要进行一次锁存,4个595芯片同时在进行锁存,各自锁存住了自己的数据。
*/
RCLK = 0; // 电尾一低一高就完成锁存了
RCLK = 1;
// 这两句之后595就完成了锁存,d1-d4就会影响4个595芯片的并行输出端,
// 进而会影响点阵中LED的正负极的值,然后LED就会亮或者灭
// 按照我们写的程序,d1和d2是负极,d3和d4是正极
}
8.横向取模的显示函数2
8.1、定义行选择数组
8.2、使用for循环进行显示
8.3、编写点阵字显示函数
#define uchar unsigned char
unsigned char zhu[32] =
{
128, 0, 136, 0, 136, 0, 248, 31,
132, 0, 130, 0, 128, 0, 255, 127,
192, 1, 160, 2, 144, 4, 136, 8,
132, 16, 131, 96, 128, 0, 128, 0
};
unsigned char hang[32] =
{
0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08,
0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80,
0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00,
0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x00
};
void main(void)
{
uchar i = 0;
for (i=0; i<16; i++)
{
SendData(~zhu[2*i+1], ~zhu[2*i], hang[2*i], hang[2*i+1]);
}
}
9.纵向取模的显示函数
9.1、先观察总结纵向取模的取模规律
9.2、显示第1列
9.3、多显示几列寻找规律
9.4、编写成函数然后实验测定
#define uchar unsigned char
unsigned char zhu[32] =
{
128, 160, 144, 142, 136, 136, 136, 255,
136, 136, 136, 136, 136, 128, 128, 0,
32, 32, 16, 8, 4, 2, 1, 255,
1, 2, 4, 8, 16, 32, 32, 0
};
unsigned char lie[32] =
{
0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfb, 0xff, 0xf7,
0xff, 0xef, 0xff, 0xdf, 0xff, 0xbf, 0xff, 0x7f,
0xfe, 0xff, 0xfd, 0xff, 0xfb, 0xff, 0xf7, 0xff,
0xef, 0xff, 0xdf, 0xff, 0xbf, 0xff, 0x7f, 0xff,
};
void main(void)
{
uchar i = 0;
for (i=0; i<16; i++)
{
SendData(lie[2*i], lie[2*i+1], zhu[i+16], zhu[i]);
// SendData(0xff, 0xfe, 32, 128); // 最左边一列
// SendData(0xff, 0xfd, 32, 160); // 左边第2列
}
}