单片机上的一小块屏幕就是LED点阵屏,与数码管一样,内部由LED灯组成,只是点阵屏使用的LED灯更多,LED灯呈矩形分布而非“8”字形;并且点阵屏和数码管一样,有两种接法共阳极和共阳极;
16*16LED点阵屏的原理图如下:
可以看到,这种每行或每列都连接在1个I/O口的方式与矩阵键盘一样,所以我们需要进行逐行扫描或者逐列扫描,也就是坐标的方式,就能够点亮我们想要点亮的灯。从原理图可以看出,每一行LED都连接到POS这个网络标号上,而POS这个网络标号连接到J28和J34。行为二极管的阳极,列为二极管的阴极。
每一列LED都连接到NEG这个网络标号上, 而原理图上没有相应的I/O口对应NEG,这是为什么?———试想,如果每一行每一列都通过I/O扣进行直接控制,那么这里就需要4*8=32个I/O口。为了减少I/O口的使用,这时候就需要用到移位寄存器74HC595:
串行输入并行输出:1个1个输入,多个同时发出;
SER:串行数据输入引脚(也就是我们是通过SER把数据1个1个地输入进去)
SRCLK : 移位寄存器时钟引脚,上升沿时(给高电平时),移位寄存器中的bit 数据整体后移(图 中灰色向下箭头方向),并接受新的bit
RCLK : 存储寄存器时钟输入引脚。上升沿时,数据从移位寄存器转存带存储寄存器(图中灰色向右箭头)
整个过程就是:通过SER往里面放数据,SERCLK给高电平一次,就把数据向下移动一位,然后再往里面放入数据,就这样一直循环,直到8个数据位全部装满,RCLK赋予高电平,锁存,把8位数据同时传送出去给QA——QH(就等于给枪上八颗子弹然后同时发射出去一样)
问题来了,如果我输入第9位数据,此时第1位数据就会被挤掉,传送给QH' ,也就是多片级联,此时的QH'就充当下一个74HC595的SER,继续存入数据:
可以看到74HC595模块内有4块芯片,也就是多片级联;74HC595(C)的输出连接到 LED 点阵前 8 列,74HC595 (D)的输出连接到 LED 点阵后 8 列。图上的 NEGx 是网络标号,与 LED 点阵列相 连。74HC595 需要用到的控制管脚 RCLK、SRCLK、SER 并未直接连接到 51 单片机 的 IO 上,而是连接到 J24
所以,如果要想 51 单片机能够控制 74HC595 输出数据,就必须将单片机管脚通过 导线连接到 J24 端子上。因此需使用 3 根杜邦线将单片机的管脚与 J24 端子连接。 由于 74HC595 模块电路是独立的,所以使用任意单片机管脚都可以
我们需要下图那样通过杜邦线进行连接,也就是说单片机的三根引脚控制J24,也就是通过电脑向74HC595模块输入数据,74HC959(A)和(B)控制的是点阵屏的行,(C)和(D )控制的是点阵屏的列。所以我们只需要向模块输入4组字节,也就是利用4块芯片就能控制LED点阵屏,行是阳极应该给予高电平,列是阴极给予低电平:
那么在程序当中我们应该怎么样去给SER输入数据:
首先进行声明之后,我们创建一个名为_74HC595_WriteByte的子函数,这里子函数里面的第一步程序是为了提取出第八位数据,也就是最高位(因为我们要进行移位的操作,所以我们要从高位开始),这里可能会有疑问:SER不是位数据吗?而赋值号右端进行按位与操作得到的不是一个字节么?
按位与&是只有当两个对应位都为1时,结果才为1,否则结果为0(比如1010 0000 & 1110 0011,那么最后结果输出就是1010 0000),0x80转化为二进制是1000 0000,也就是说Byte无论是什么,其只要与0x80进行按位与操作,最后的结果只能是1000 0000 或者0000 0000,也就是只有最高位在变化,SER是位数据,赋值号右边只有不等于0,那么SER就等于1;也就是说,如果赋值号右边是1000 0000(0x80)时,SER = 1;如果赋值号右边最终结果是0000 0000时,SER= 0;就是通过这样的方式把Btye的第八位数据提取出来,第八位数据是1的话就输出1,第八位数据是0的话就输出0,接下来的Byte的每一位提取我们都使用这个方法,也就是利用到按位与的掩码操作。
单片机引脚默认高电平,所以我们需要在主函数中将Rclk置0先,才能给它高电平;
注意:这里的Rclk如果定义成RCLK就会出现重复定义的情况,因为头文件里有相应的RCLK的定义了
接下来Byte的第八位数据提取完成,我们要提取Btye的第七位数据:
如此循环,就能提取出Byte的第六位第五位第四位.....
所以我们考虑到,添加一个循环:
这样循环8次就相当于把每一位都移放好了,都放在移位寄存器里面了,接下来我们就需要使Rclk锁存,将它同时输出:
以上是输入1组数据的操作,我们需要的是输入4组数据,并且先输入顺序应是:HC595(D)—(A):
一、LED点阵(点亮一个点)
有了上述基础,点亮一个点只需要在主函数里面,把Btye1、2、3、4数据化即可:
比如我要点亮左上角的灯,也就是第一行第一列的灯,那么我需要给予第一行高电平,其余给0;第一列给低电平,其余给高电平。也就是Byte4 =1111 1111 (0xFF),Byte3= 1111 1110(0xFE),Byte 2 = 0000 0000(0x00),Byte1 = 0000 0001(0x01)
代码如下:
#include "Delay.H"sbit Rclk = P3^4;
sbit SRCLK = P3^5;
sbit SER = P3^6;void _74HC595_WriteByte(unsigned char Byte1,Byte2,Byte3,Byte4)
{unsigned char i;for(i=0;i<8;i++){SER = Byte4&(0x80>>i);//0x80右移i位,然后再与Byte进行按位与,再赋值给SERSRCLK = 1; //移位寄存器时钟上升沿将端口数据送入寄存器中Delay(10);SRCLK = 0;//复位Delay(10);}for(i=0;i<8;i++){SER = Byte3&(0x80>>i);SRCLK = 1;Delay(10);SRCLK = 0;Delay(10);}for(i=0;i<8;i++){SER = Byte2&(0x80>>i);SRCLK = 1; Delay(10);SRCLK = 0;Delay(10);}for(i=0;i<8;i++){SER = Byte1&(0x80>>i);SRCLK = 1; Delay(10);SRCLK = 0;Delay(10);}Rclk = 1; //存储寄存器时钟上升沿将数据同时输出到存储器中Rclk = 0;//复位}
void main()
{ SRCLK = 0;//单片机引脚默认高电平,先置0才能给它赋予高电平Rclk = 0; while(1){_74HC595_WriteByte(0x01,0x00,0xFE,0xFF);}}
二、LED点阵(显示数字)
有了上面的基础,我们知道点亮某一个LED很简单,点亮某一行或者某一列也很简单,只需要根据原理图给出4组字节即可,但是如果是实现不同行不同列位置“同时”点亮,是行不通的,就像数码管一样,普中的数码管不可能实现几个同时亮
要实现行列不同位置亮灯,需要使用动态显示的方法,也要结合扫描的方法。 在第一行亮灯一段时间以后灭掉,点亮第二行一段时间以后灭掉,点亮第三行一 段时间以后灭掉,如此点亮,直到八行全部点亮一次,在第一行点亮到最后一行 灭掉的总时间不能超过人肉眼可识别的时间,即 24 毫秒。在每一行点亮的时候, 给列一个新的数据,此时对应列的数据就可以体现在这行上要点亮的灯上。也就是说这里的“同时”只是通过闪烁的时间足够快,从而达到同时亮的效果。
点亮一个灯很简单,我们可以通过原理图判断出该输入什么数据给HC595,但是我如果想要显示数字/动画,我不可能一个灯一个灯地去判断应该输入什么数据,这里我们需要用到另外一个工具:
然后可以在这个 16*16 白色格子里面点击,点击后即会在对应位置出现 一个黑点,表示在 LED 点阵对应位置的 LED 灯点亮,未点击位置(白色)表 示 LED 点阵对应位置的 LED 灯熄灭
然后点击“取模方式”,选择 C51 格式选项,然后在点阵生成区自动会 生成数字字符对应的数据(如果是使用汇编编程,那么汇编对应的汉字数据可选择 A51 格式)。
这些数据其实就是上述描绘的数字 0 从上到下依次每行对应的列数据