🚩 WRITE IN FRONT 🚩
🔎 介绍:"謓泽"正在路上朝着"攻城狮"方向"前进四" 🔎
🏅 荣誉:2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评百大博主、华为云享专家、阿里云专家博主、掘金优秀创作者、全网粉丝量7w+、全网访问量100w+🏅
🆔 本文章内容由 謓泽 原创 如需相关转载请提前告知博主 ⚠
📝 个人主页:謓泽的博客 📃
📣 专栏系列:【51单片机】系列 📃
🧑💻 Gitee:謓泽 (wsxsx) - Gitee.com ⭐️
🎁 点赞👍+ 收藏⭐️+ 留言📝
✉️ 我们并非登上我们所选择的舞台,演出并非我们所选择的剧本 📩
💬 本系列哔哩哔哩江科大51单片机的视频为主以及博主自己码字的总结梳理 📚
[LED]点阵屏介绍
🔥LED点阵屏组成是由〖若干个独立的LED灯组成以矩阵的形式排列〗
🍅**拓展→**在我们用的普中开发板上它是以贴片LED灯以 8*8 的矩阵的形式排列。
🌴**注⇢**在我们的单片机上 LED灯 它是以排成 8*8 的矩阵形式来进行排列的。
适用场合
- Ⅰ 显示文字。
- Ⅱ 图片。
- Ⅲ 视频。
在上述只是LED点阵适应的一小部分场合实际上来说它的应用场合可以说是非常多的都是我们大街小巷都能见到它的身影的。例如:我们都知道的广告牌上的流动的灯光、公共场合、夜光灯等等...
颜色分类
⒈单色(在我们的开发板上的点阵屏就是单色只能显示红色的)
⒉双色(就比如说我们下图中间的图,它就是双色点阵)
⒊全彩(全彩就像是右边图一样都有红、绿、蓝三种光三原色)
像素分类
⒈ 8*8 【当然这个点阵你想做,任意像素都是可以的】
⒉16*16【一般情况下列和行设置⑧的倍数,因为①个字节=⑧位比特位】
📑知识点补充-【在我们编写程序的时候就是⒏位为一组灯的形式来编写程序的充分利用⒈字节来显示。所以一般我们把它分为⑧的倍数保证都能对应到它的像素上】
🌴**注⇨**大规模的LED点阵屏,通常由很多个小点阵拼接而成。
显示原理
在LED点阵屏的结构类似于数码管 ,只不过是数码管把每一列的像素以**"8"**字型排列而已。
注→在第③个图当中是双色点阵。
LED点阵屏与数码管一样,有共阴和共阳两种接法,不同的接法对应的电路结构不同。
🍏**拓展⇢**对于单色的点阵来说共阴还是共阳实际上都是一样的,但是对于双色的点阵来说它是具体来分共阴和共阳的。
LED点阵屏需要进行逐行 或逐列扫描,才能使所有LED同时显示。当然你也可以同时用64个IO口来对这个LED点阵进行控制这样它就不需要进行扫描。但是,64个IO口太浪费了,所以我们连成这种矩阵的形式。
**🍊缺点→**当然连成矩阵的形式的缺点是和数码管一样每次只能选择⒈行或者⒈列。
⬛点阵屏显示原理如下👇
我们给第一行高电平(1)的话就可以让这一行的LED灯被选中[相当于数码管的位选],而其它行的LED灯是不能够点亮的。就是说我们是不能够同时对LED灯显示不同的内容。
如果我们第一行给上(1)高电平,第一列给上(0)低电平的话。那么我们第一个LED灯就会点亮[这个是逐行扫描],逐列扫描也是一样的。
**如上图所示⇢**这是点阵的结构图,左边是点阵的一个实物图,点阵下面有一些引脚。一般8*8点阵的它一般有16个引脚。而右边的双色点阵呢,一般都是比它多了⑧个引脚。那么看中间这幅图就是它里面的一个结构,就是每一个LED把它连成的是矩阵的形式,并且把每一行阳极给引出来,也就是每一行阳极连在了一起,每列的阴极连在了一起。
🍋**注→**我们给定一行的话假设让那列的第一个第二个灯点亮,我还想让第二行的第一个灯第二个灯也亮。这个是不可以的我们不能做到二行或二行同时分别点亮所在行的灯,但是我们可以适用动态显示也可以达到这样的效果。但是可以一行给多个灯让它进行点亮,同理逐列扫描也是同理的。
🎄原理图
左边的这幅图是LED点阵屏的原理图。
右边的这幅图是74HC595它也是我们点阵屏所要操作的一部分 。
那么为什么会出现这个74HC595呢?
原因是⇢即使这⒍⒋个灯按照逐行逐列扫描来进行连接的话,它仍然需要有⒈⒍个IO口才可以满足要求。这个对于我们的开发板来说实际上IO口是很紧张的,因为我们有这么多的一个设备。所以我们要对它进行一个IO口的一个扩展,那么它是怎么样进行一个扩展的呢?
答⇢我们看到上面图中的点阵原理图排列相当于一种有顺序的排列没有怎么乱,不同的点阵它的一个引脚排序是不同的。由于原理图上没写哪个是行或者是列,这里写程序的时候测试了一下,如下图所示↓
⒈D0~D7连接的是行为阳极[控制它的行]。
⒉P0~P7连接的是列为阴极[操作P0口就可以直接控制它的列]
那么大家对应这幅图的话就知道它内部的连接关系,在写程序时候也会更加方便。
那么它的原理图当中的行是D0~D7,这些都是网络编号都是连接到我们的74HC595位移缓存器,它没有直接连到我们的IO口上。
左边用P35、P36、P34 来控制右边的D0~D7的输出。
注→⒈在上述原理图上面还有两个跳线帽在这里JOE是要把OE和GND用跳线帽连接在一起因为这个才能让74HC595的OE(非)输出使能位低电平这样74HC595工作。
注→⒉OE(非)使能端 像一些寄存器 计时器都有OE使能端的都可以通过给OE赋值起到控制作用。
[74HC595]的操作
如何利用这三根线(P35、P36、P34)来操作这些输出端。
74HC595是串行输入并行输出的移位寄存器,可用3根线输入串行数据,8根线输出并行数据,多片级联后,可输出16位、24位、32位等,常用于IO口扩展。如下图[模式图]所示↓
⒈SER**[14]**(P34)⇢串行数据 ,因为我们知道我们的数据分为串行、并行。如果是串行的话,我们的数据是一个一个出去的,就相当于我们的串口一样,在时钟的激励下一个一个出去的。如果是并行的话就同时输出⑧根线。工作模式:首先给这个左边的这个一位寄存器进行输入,右边是这个输出缓存,那么这个串行数据就进来,给这个一位数据。我们在这里给的值因该是第⑧位bit给上高电平。注→在一开始的时候我们应该要给最高位置1,这个是我们给第一个数据的,它会被存放在最高位当中去。同理。
拓展⇥这里之所以刚开始是最高位给上高电平是因为数据是从高到低的,如果你是从低到高的话那么最终效果就会是反着来的。
那么这个一位数据怎么走的,那么就是这个如下↓
⒉SERCLK**[11](P36)⇢** 叫做串行时钟** ,就是它时钟每次来一个上升沿(低电平~高电平)高电平有效,我们的这个数据就会每次向下进行一次的移位。SCK走完之后怎么样控制输出就有一个[RCLK]上升沿所存控制。
⒊RCLK[12]**(P35)⇢寄存器时钟 ,把这⑧位的数据同时搬运到右边的输出缓存区去。就能够达到串行输入并行输出效果。当我们所有的数据对应位全部都好了的话,控制所有的芯片来一个上升沿**所存(RCLK)那么在SER当中的("1")或("0")就会同时被输出到对应的芯片上的输出缓存当中里面。
多片级联
QH'⇢ 多片级联。如果我们继续移位的话,想要刷新它的数据,假设我们把SER给上一个数据(1)再给串行时钟一个高电平,那么这个(1)就会被移动到左边的寄存器去。同时呢最后一位寄存器上的数字就会被移动到QH'上去接到下一个74HC595的SER中。那么这个移出去的就会被移动到下一个寄存器第一位bit当中,如果再移动的话,那么第一位的1就会往下移动。以此类推从而达到多片级联效果。
注→多片级联的话SERCLK和RCLK 是要连接到下一块的74HC595的芯片上的。
这样就可以做IO口的扩展了,只要你的时钟速度够快,没有太高操作速度的要求。当然这样的话,它的IO口的速度会减慢。当然这也是必须的,你不可能三根线转八根线的话不可能还是那么快的对吧。
VCC 和 GND 就不多说了,必须要有这个才能进行供电,你的芯片才能工作。
QA~QH 这是它的一个输出端,⑧位并行数据。
左边就是它的输入端的一个控制引脚。
OE加①横线,一般这个芯片加了的话都代表它是低电平有效或者下降沿(高电平~低电平 )就是说我们OE(输出使能)横线给上低电平。就是说我们OE如果接上了低电平它才可以进行一个输出,就相当于开关一样,OE接上低电平芯片可以工作,接高电平OE工作不起来。
OE横线是接在了我们的跳线帽上,所以在用的时候,要把我们的595接在 OE和GND!
P35这个引脚叫做 RCLK:这个引脚就是它的 寄存器时钟,这个功能等下讲解。
SRCLR:这个东西叫做 串行清0端,它会把里面数据进行清空,在这里直接接上了Vcc代表它不进行清空。
SRCLK:叫做串行时钟 ,SER:叫做串行数据, RCLK:寄存器时钟,主要是用这三个引脚来控制这右边的输出。
注意:我们单片机IO输出是弱上拉模式,当高电平输出的话。实际还是加了一个电阻再是Vcc的,这样电流就会减小,低电平的时候是不变的。证明:低电平强,高电平弱。所以我们单片机是需要驱动模式的。
C51的sfr、sbit
**sfr:**特殊功能寄存器声明特殊功能寄存器声明。我们操作单片机的硬件电路都是通过操作单片机的寄存器来进行实现的,也就是它是由特殊的十六进制构成的这个寄存器声明的。
例:sfr P0 = 0x80; 声明P0口寄存器,物理地址为0x80。注→寄存器是声明⑧为地址的。
**sbit:**特殊位声明是声明地址的某一位。.
例:sbit P0_1 = 0x81; 或 sbit P0_1 = P0^1;声明P0寄存器的第1位。
声明位的时候,我就可以对某一位单独操作,从而不影响其它位。
可位寻址/不可位寻址:在单片机系统中,操作任意寄存器或者某一位的数据时,必须给出其物理地址,又因为一个寄存器里有8位,所以位的数量是寄存器数量的8倍,单片机无法对所有位进行编码,故每8个寄存器中,只有一个是可以位寻址的。对不可位寻址的寄存器,若要只操作其中一位而不影响其它位时,可用"&=(与等于对某一位进行清0)"、"|=(或等于对某一位数据进行或1任何数或1都为1)"、"^=(异或等于对某一位进行异或,如果不一样就置1,一样就置0)"的方法进行位操作相当于可以对某一位进行取反。
注→不可进行重定义哟(●'◡'●)
程序示例
⒈用LED点阵屏⑧×⑧显示钻石💎图形案。
注→在下放程序当中是以列为位选、行为段选和数码管是类似的。当然在我们取图案的时候你可以用文字取模软件这样会方便许多会自动生成你想要对应的十六进制的数据,这样就不需要你进行计算了。记得要纵向取模因为我们这个是把输入列的形式排成列来的。
如图所示📝示例代码如下↓
cpp#include <REGX52.H> #include "Delay.h" const unsigned char Array[] = {0x38,0x44,0x82,0x81,0x81,0x82,0x44,0x38};//显示钻石 sbit RCK=P3^5; //RCLK 寄存器时钟 sbit SCK=P3^6; //SRCLK 串行时钟 sbit SER=P3^4; //SER 串行数据 #define MATRIX_LED_PORT P0 //列,注→P0口不是位所以不能用sbit,此时我们就可以使用宏定义代替 /** * @brief 74HC595写入一个字节相当于给LED点阵屏行的引脚赋值 * @param Byte 要写入的字节 * @retval 无 */ void _74HC595_WriteByte(unsigned char Byte) { unsigned char i = 0; for(i=0;i<8;i++)//循环八次使得串行数据可以输入到右边的缓冲区 { SER=Byte&(0x80>>i);//按位右移 每次都给上高电平 SCK=1;//上升沿进行移位,给上升沿 SCK=0;//清零,每次移除一位即可!为下一次做准备 } RCK=1;//循环结束后再给一次上升沿 RCK=0;//上升沿清0 } /** * @brief LED点阵屏显示一列数据 * @param Column 要选择的列,范围:0~7,0在最左边 * @param Data 选择列显示的数据,高位在上,1为亮,0为灭 * @retval 无 */ // 列 行 void MatrixLED_ShowColumn(unsigned char Column,Data) { _74HC595_WriteByte(Data); //调用函数 行高电平点亮 P0 =~(0x80>>Column); //0111 1111 列低电平点亮 按位右移 Delay(1); //延时1s P0 =0xFF; //清影 1111 1111 } /** * @brief LED点阵屏初始化 * @param 无 * @retval 无 */ void MatrixLED_Init() { SCK=0;//上电后都是高电平!所以,我们要给它低电平。防止数据传输移位冲突。 RCK=0;//上电之后上升沿清0 } int main(void) { unsigned int i = 0; int sz = sizeof(Array) / sizeof(Array[0]); MatrixLED_Init(); while(1) { for(i=0;i<sz;i++) { // 列 行 MatrixLED_ShowColumn(i,Array[i]); } } }
⒉用LED点阵屏⑧×⑧形式流动动画显示字母CSDN
📝示例代码如下↓
main.c - 主函数
cpp#include <REGX52.H> #include "Delay.h" #include "MatrixLED.h" //code 定义数据内存更大ROM,而且数组内的内容是"只读"属性的。 //动画数据 unsigned char code Animation[]={ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x00, 0xE1,0xF1,0x99,0x8F,0x87,0x00,0x00,0xFF, 0x81,0x81,0x81,0x81,0x7E,0x00,0x00,0xFF, 0x06,0x0C,0x18,0x30,0xFF,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; int main(void) { //Offset 取下一帧数据(偏移量) count用于进行扫描 unsigned char i,Offset=3,Count=0; const int sz = sizeof(Animation)/sizeof(Animation[0]); MatrixLED_Init(); while(1) { for(i=0;i<8;i++) //循环8次,显示8列数据 { MatrixLED_ShowColumn(i,Animation[i+Offset]); } Count++; //每次扫描一次Count++ if(Count>5) //Count 一帧扫描5遍,扫描五遍就把起始位置向下面移动一帧. { Count=0; Offset++; //偏移+1,显示下一列画面。offset+=8可以显示一帧画面 if(Offset>sz-8) //当Offset为sz-8的时候数组会溢出,所以此时我们要进行清0 { Offset=0; } } } }
Delay.h & Delay.c
cpp//Delay.c void Delay(unsigned int xms) { unsigned char i, j; while(xms--) { i = 2; j = 239; do { while (--j); } while (--i); } } //Delay.h #ifndef __DELAY_H__ #define __DELAY_H__ void Delay(unsigned int xms); #endif
MartrixLED.c & MartrixLED.h
cpp//MartrixLED.c #include <REGX52.H> #include "Delay.h" sbit RCK=P3^5; //RCLK sbit SCK=P3^6; //SRCLK sbit SER=P3^4; //SER #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++) { SER=Byte&(0x80>>i); SCK=1; SCK=0; } RCK=1; RCK=0; } /** * @brief 点阵屏初始化 * @param 无 * @retval 无 */ void MatrixLED_Init() { SCK=0; RCK=0; } /** * @brief LED点阵屏显示一列数据 * @param Column 要选择的列,范围:0~7,0在最左边 * @param Data 选择列显示的数据,高位在上,1为亮,0为灭 * @retval 无 */ void MatrixLED_ShowColumn(unsigned char Column,Data) { _74HC595_WriteByte(Data); MATRIX_LED_PORT=~(0x80>>Column); Delay(1); MATRIX_LED_PORT=0xFF; } //MartrixLED.h #ifndef __MATRIX_LED_H__ #define __MATRIX_LED_H__ void MatrixLED_Init(); void MatrixLED_ShowColumn(unsigned char Column,Data); #endif