3.7 LED 16*16点阵
3.7.1 点阵原理图
LED点阵使用连线说明:P595_A接J17、P595_B接J18、JP595跳线帽需要接上、JP1302断开、J11(P3.3)断开
从上面LED点阵原理图中看出,点阵里的每个LED的阴端接在行上面,阳端接在列上面。
点亮一个LED二极管只需要根据正负极给对应的电源即可发光。
根据实验板的16*16点阵原理图来看,只要给横向(行)16个IO输出低电平,纵向(列)16个IO口输出高电平,即可点亮点阵上面的所有LED。
3.7.2 点阵原理介绍
实验板上的16*16点阵是由4个8*8LED点阵组成的,一个 8*8 的点阵是由 64个LED小灯组成。
16*16点阵分别由4片74HC595控制,4片74HC595采用级联的方式连接。
3.7.3 74HC595缓存器
74HC595是一个8位串行输入、平行输出的位移缓存器:平行输出为三态输出。
在SCK的上升沿,单行数据由SDL输入到内部的8位位移缓存器,并由Q7‘输出,而平行输出则是在LCK的上升沿将在8位位移缓存器的数据存人到8位平行输出缓存器。
当串行数据输人端OE的控制信号为低使能时,平行输出端的输出值等于平行输出缓存器所存储的值。而当OE为高电位,也就是输出关闭时,平行输出端会维持在高阻抗状态。
74HC595支持级联,当多个595级联到一起时,通过数据线发送的一个数据最终会移位给最后的一个595。
因为级联数据是被挤出到下一级的,所以先发送的数据最后是到最后一级的595。
每次向74HC595发送一个字节,74HC595最先收到的一位数据是高位,也就是最先收到的数据就到达Q7脚,如果我们先输出数据高位的话,最高位在8个脉冲后就会跑到Q7脚(数据脚最高位)。这就像我们排队一样,一个寄存器里面有8个位置,每次给一个脉冲就好比一次呼叫:“大家可以往前移一位了!”就这样,队伍不断得往前移。
这个过程就像在向前挤一样:
通过真值表得知:
发送一个数据0的时序代码如下:
HC595_DATA=0;HC595_SCK=0;HC595_SCK=1;
发送一个数据1的时序代码如下:
HC595_DATA=1;HC595_SCK=0;HC595_SCK=1;
3.7.4 示例代码: LED点阵单点扫描显示
下面代码实现,点阵依次从左上角点亮一个点,等待一段时间,再关闭所有点,再点亮第二个点,等待一段时间,再关闭所有点,从左到右,从上到下,依次把所有点亮都点亮一遍。
1.#include <reg51.h> //定义HC595使用的IO口 sbit HC595_DATA=P3^4; //串行数据输出 sbit HC595_RCK=P3^5; //存储寄存器时钟 sbit HC595_SCK=P3^6; //移位寄存器时钟 /* 通过HC595发送一个字节数据 先发送高位,在HC959上对应的是低位(也就是LED点阵的最左边) */ void HC595_Send_Byte(u8 byte) { u8 i; for(i=0;i<8;i++) { if(byte&0x80)HC595_DATA=1; else HC595_DATA=0; HC595_SCK=0; HC595_SCK=1; byte<<=1; //依次发送高位 } } /* 将HC959存储器里的数据输出到总线上 */ void HC595_DataOut(void) { //将移位存储器的输出并行输出到总线上 HC595_RCK=0; HC595_RCK=1; HC595_RCK=0; } /* HC595的3、4级是控制点阵的列 (低电平) HC595的1、2级是控制点阵的行 (高电平) 给HC595第1级发送的数据会移动到最后一个HC595。 */ int main() { u8 i,j; u16 Row; //行 u16 col; //列 while(1) { for(i=0;i<16;i++) { Row=0x0000; Row|=1<<i; //行 for(j=0;j<16;j++) { col=0xFFFF; col&=~(1<<j); //列 HC595_Send_Byte(col>>8); HC595_Send_Byte(col); HC595_Send_Byte(Row>>8); HC595_Send_Byte(Row); HC595_DataOut(); DelayMs(100); //延时一段时间,再关闭所有显示 HC595_Send_Byte(0xFF); HC595_Send_Byte(0xFF); HC595_Send_Byte(0x00); HC595_Send_Byte(0x00); HC595_DataOut(); } } } }
3.7.5 示例代码: 控制LED点阵显示指定效果
(1). 实现点阵按照指定时间间隔全亮和全灭
#include <reg51.h> int main() { u8 i,j; while(1) { //下面4行代码,开启点阵 HC595_Send_Byte(0x00);//4列: 低位对应右边第8列开始,8~15 HC595_Send_Byte(0x00);//3列: 低位对应左边第0列开始,0~7 HC595_Send_Byte(0xFF);//2行: 低位对应上面第8行,依次8~15 HC595_Send_Byte(0xFF);//1行: 低位对应上面第0行,依次0~7 HC595_DataOut(); DelayMs(500); //下面4行代码,关闭点阵 HC595_Send_Byte(0xFF);//4列: 低位对应右边第8列开始,8~15 HC595_Send_Byte(0xFF);//3列: 低位对应左边第0列开始,0~7 HC595_Send_Byte(0x00);//2行: 低位对应上面第8行,依次8~15 HC595_Send_Byte(0x00);//1行: 低位对应上面第0行,依次0~7 HC595_DataOut(); DelayMs(500); } }
(2). 实现点阵旋转效果,按照8*8为单位,点亮4个小方块
点亮的顺序,看下面代码的注释。
int main() { while(1) { //点亮左上角8*8方块 HC595_Send_Byte(0xFF); HC595_Send_Byte(0x00); HC595_Send_Byte(0x00); HC595_Send_Byte(0xFF); HC595_DataOut(); DelayMs(100); //点亮右上角8*8方块 HC595_Send_Byte(0x00); HC595_Send_Byte(0xFF); HC595_Send_Byte(0x00); HC595_Send_Byte(0xFF); HC595_DataOut(); DelayMs(100); //点亮右下角8*8方块 HC595_Send_Byte(0x00); HC595_Send_Byte(0xFF); HC595_Send_Byte(0xFF); HC595_Send_Byte(0x00); HC595_DataOut(); DelayMs(100); //点亮左下角8*8方块 HC595_Send_Byte(0xFF); HC595_Send_Byte(0x00); HC595_Send_Byte(0xFF); HC595_Send_Byte(0x00); HC595_DataOut(); DelayMs(100); } }
3.7.6 示例代码: 控制LED点阵显示汉字
使用LED点阵显示汉字,需要用到汉字取模软件,本章节使用的取模软件“PCtoLCD2002”,软件的取模方式配置如下。
示例代码: 在LED点阵上显示一个中文
#include <reg51.h> //定义HC595使用的IO口 sbit HC595_DATA=P3^4; //串行数据输出 sbit HC595_RCK=P3^5; //存储寄存器时钟 sbit HC595_SCK=P3^6; //移位寄存器时钟 /* 通过HC595发送一个字节数据 先发送高位,在HC959上对应的是低位(也就是LED点阵的最左边) */ void HC595_Send_Byte(u8 byte) { u8 i; for(i=0;i<8;i++) { if(byte&0x80)HC595_DATA=1; else HC595_DATA=0; HC595_SCK=0; HC595_SCK=1; byte<<=1; //依次发送高位 } } /* 将HC959存储器里的数据输出到总线上 */ void HC595_DataOut(void) { //将移位存储器的输出并行输出到总线上 HC595_RCK=0; HC595_RCK=1; HC595_RCK=0; } /* 在LED点阵上显示一个16*16数据 */ void LED_DisplayData(u8 *p) { u8 i; u16 Row; //控制行的状态,每次只点亮1行 for(i=0;i<16;i++) { Row=0x0000; Row|=1<<i; HC595_Send_Byte(*(p+1)); //控制列右8个IO HC595_Send_Byte(*p); //控制列左8个IO p+=2; HC595_Send_Byte(Row>>8); //控制行下8个IO HC595_Send_Byte(Row); //控制行上8个IO HC595_DataOut(); } } u8 code font[] = {0x7F,0xFF,0x7F,0xFF,0x7F,0xFF,0x7F,0xFF,0x03,0xE0,0x7B,0xEF,0x7B,0xEF,0x7B,0xEF,0x7B,0xEF,0x7B,0xEF,0x03,0xE0,0x7B,0xEF,0x7F,0xFF,0x7F,0xFF,0x7F,0xFF,0x7F,0xFF};/*"中",0*/ int main() { while(1) { LED_DisplayData(font); //显示中文 } }
3.7.7 示例代码: 控制LED点阵显示多个中文
下面代码实现,在LED点阵上循环显示多个文字或者图案,所有的功能都使用子函数封装,调用逻辑清晰。
#include <reg51.h> //定义HC595使用的IO口 sbit HC595_DATA=P3^4; //串行数据输出 sbit HC595_RCK=P3^5; //存储寄存器时钟 sbit HC595_SCK=P3^6; //移位寄存器时钟 /* 通过HC595发送一个字节数据 先发送高位,在HC959上对应的是低位(也就是LED点阵的最左边) */ void HC595_Send_Byte(u8 byte) { u8 i; for(i=0;i<8;i++) { if(byte&0x80)HC595_DATA=1; else HC595_DATA=0; HC595_SCK=0; HC595_SCK=1; byte<<=1; //依次发送高位 } } /* 将HC959存储器里的数据输出到总线上 */ void HC595_DataOut(void) { //将移位存储器的输出并行输出到总线上 HC595_RCK=0; HC595_RCK=1; HC595_RCK=0; } /* 在LED点阵上显示一个16*16数据 */ void LED_DisplayData(u8 *p) { u8 i; u16 Row; //控制行的状态,每次只点亮1行 for(i=0;i<16;i++) { Row=0x0000; Row|=1<<i; HC595_Send_Byte(*(p+1)); //控制列右8个IO HC595_Send_Byte(*p); //控制列左8个IO p+=2; HC595_Send_Byte(Row>>8); //控制行下8个IO HC595_Send_Byte(Row); //控制行上8个IO HC595_DataOut(); } } /* 关闭点阵所有点 */ void LED_Clear(void) { HC595_Send_Byte(0xFF);//4列: 低位对应右边第8列开始,8~15 HC595_Send_Byte(0xFF);//3列: 低位对应左边第0列开始,0~7 HC595_Send_Byte(0x00);//2行: 低位对应上面第8行,依次8~15 HC595_Send_Byte(0x00);//1行: 低位对应上面第0行,依次0~7 HC595_DataOut(); } code u8 buff[][32]= { {0xFF,0xFF,0xFF,0xFF,0x00,0x80,0xDF,0xFF,0xDF,0xFF,0xDF,0xFF,0x1F,0xF0,0xDF,0xF7,0xDF,0xF7,0xEF,0xF7,0xEF,0xF7,0xF7,0xF7,0xF7,0xF7,0xFB,0xF7,0xFD,0xFA,0xFE,0xFD},/*"万",0*/ {0xEF,0xFF,0xEF,0xC1,0xEF,0xDD,0x00,0xED,0xEF,0xED,0xEF,0xF5,0x81,0xED,0xEF,0xED,0xEF,0xDD,0x00,0xDD,0xEF,0xDD,0xF7,0xE9,0xF7,0xF5,0xFB,0xFD,0xFD,0xFD,0xFE,0xFD},/*"邦",1*/ {0x0F,0xF0,0xEF,0xF7,0xEF,0xF7,0x0F,0xF0,0xEF,0xF7,0xEF,0xF7,0x0F,0xF0,0xDF,0xFF,0xEF,0xFF,0x07,0xC0,0xBB,0xDD,0xBD,0xDD,0xDF,0xDE,0xEF,0xDE,0x77,0xEB,0xBF,0xF7},/*"易",2*/ {0x7F,0xFF,0x7B,0xEF,0x7B,0xEF,0x03,0xE0,0xFF,0xFB,0xBB,0xFB,0xBB,0x83,0x00,0xBD,0xBB,0xD6,0xBB,0xF7,0x83,0xF7,0xBB,0xF7,0xBB,0xEB,0x83,0xEB,0xBB,0xDD,0xFF,0xBE},/*"嵌",3*/ }; #define TIME 100 int main() { u8 i,j; while(1) { for(i=0;i<sizeof(buff)/sizeof(buff[0]);i++) { for(j=0;j<TIME;j++) //停留的时间 { LED_DisplayData(buff[i]); //显示中文,需要循环调用 } LED_Clear(); //清除显示 } } }