单片机学习笔记---LED点阵屏显示图形动画
目录
LED点阵屏显示图形
LED点阵屏显示动画
最后补充
上一节我们讲了点阵屏的工作原理,这节开始代码演示!
前面我们已经说了74HC595模块也提供了8个LED,当我们不使用点阵屏的时候也可以单独使用74HC595,这8个LED可以用来测试74HC595。
那接下来我们可以先不使用点阵屏,先用这8个LED来测试一下74HC595。
LED点阵屏显示图形
新创建一个工程:LED点阵屏显示图形。
写上框架
接下来先补充几个关键词的介绍:
sfr(special function register):特殊功能寄存器声明
例:sfr P0 = 0x80;
声明P0口寄存器,物理地址为0x80
sbit(special bit):特殊位声明
例:sbit P0_1 = 0x81; 或 sbit P0_1 = P0^1;
声明P0寄存器的第1位
可位寻址/不可位寻址:在单片机系统中,操作任意寄存器或者某一位的数据时,必须给出其物理地址,又因为一个寄存器里有8位,所以位的数量是寄存器数量的8倍,单片机无法对所有位进行编码,故每8个寄存器中,只有一个是可以位寻址的。对不可位寻址的寄存器,若要只操作其中一位而不影响其它位时,可用“&=”、“|=”、“^=”的方法进行位操作。
“&=”一般用于对某一位清零
“|=”一般用于对某一位置1
“^=”异或等于,一般用于对某一位取反,不一样就置1,一样就置0。异或这个用的比较少。
为什么要补充这几个关键词的介绍呢?是因为我们接下来要操作P35等三个引脚,但是为了方便操作,我们想要对这三个引脚重新命名,所以我们就要使用重命名的方法。
比如我们要重新命名P3的第五位,可以这样写
这样我们之后操作RCLK就等于操作P3的第5位
但是由于RLCK已经被头文件定义过了,所以我们不能重复定义,所以把这三个引脚都重新命名为:
不同的人对同一个引脚的命名也不一样,所以今后在芯片手册上要注意对应。
接下来我们写一个子函数,它的作用是将参数传过来的数据给写入那8个引脚,相当于给那8个引脚赋值。
由于推进来的第一位是要赋值给QH的,所以我们要先将参数的字节的最高位取出来
PS:SER是一位数据,如果将8位数据赋给它的话,非0即1
按照上一篇博客讲的工作原理,接下来要先给SERCLK初始化,
然后再给它一个高电平(上升沿),
SER过来的数据在移位寄存器中下降一位之后再将SERCLK清零,为下一次移位做准备。
到这里Byte的最高位已经操作完毕,接下来是移动次高位
同理,其他位也是这样操作,如此循环,我们可以用一个for循环语句
移完8位数据后,要输出就得先初始化RCLK锁存数据,
然后给它一个上升沿,
最后同时输出8位数据后,给它清零
这样操作完后,就能把8位数据送到IO口上了。
最后我们调用这个子函数测试一下
最后整个代码是这样的
编译无错误后看一下效果
的确这八个LED一半亮一半灭
测试74HC595完毕,接下来我们就开始实际操作点阵屏
我们会用到Delay函数的代码,所以要添加进本节的工程文件中。
由于点阵屏的操作和数码管的操作类似,所以我可以参照数码管的那一节代码。
这是我们之前写一个数码管的子函数,location表示它显示的位置,Number表示显示的段码
同理,我们把点阵屏的每一列看成location,每一行看成是段码
那么我们要添加一个子函数,column表示列,data表示行的数据
然后我们在这里先调用一下行的数据
然后要选择列的数据
这样写表示如果用户选的是第0列,也就是P07列,
那么就让P07=0,其他位等于1,所以就取反0x80=0111 1111
如果选P06=0,要如此循环写if语句,我们可以用另一种写法避免重复写if语句
然后调用一下这个函数
完整代码:
编译无错误测试一下
的确是第0列,行显示亮灭亮灭。
如果想要进行扫描的话,也需要进行消影,和数码管的操作类似(之前我们说数码管进行段选-位选-段选-位选-段选-位选的过程中,刚把下一位段选送入的时候,它会把这个段选送到上一位选,因为上一位的位选还没来得及改变,产生串位的现象,也就是残影的问题)
那么我们需要加一个过程,就是位选后,位选清零。
于是就变成段选-位选-位清零-段选-位选--位清零-段选-位选
顺便加一段延时过程
段选-位选-延时-位清零-段选-位选-延时-位清零-段选-位选
这样即使它发生串位也是串到了位清零的时刻,但是位清零什么都没有显示,这样就能达到消影的效果。
但是这样的结果是它执行一次就会马上灭掉,所以我们把调用放在while循环里面让它循环执行。
这样我们还可以显示多位了,比如让它显示一条对角的斜线
如果我们想要想让显示一个笑脸的话,可以先把效果脸的图画在一个表格中
上图画1的位置点亮,其他不亮
我们要一列一列的进行扫描,把每一列的数据转换成16进制
每一列的数据转换好之后写进去
效果
成功显示了一个笑脸
然后我们对这个代码进行一下优化
重定义一下P0
然后把我们程序中的所有P0口改成MATRIX_LED_PORT
增加这行代码的意义在于当我们用不同开发板(有可能不同引脚)运行程序时,我们只需要修改#define MATRIX_LED_PORT P0后面的P0就行,而无需逐个修改整个程序中的P0。
优化后的代码加上注释:
PS: void MatrixLED_ShowColumn(unsigned char Column,Data)这个子函数这里给1为亮,0为灭是因为:
根据点阵屏的原理图,列是把每列LED的阴极连在一起,行是把每行LED的阳极两在一起,当阴极(列)给0,阳极(行)给1的时候,对应的LED才会亮)
因此段选==选择行给了低电平之后,位选==选择列就要给高电平1才能让对应的LED亮。
第一个代码演示完毕,源码放在评论区了,自取!
LED点阵屏显示动画
接下来我们演示第二个示例代码:显示动画
把第一个代码演示的工程文件复制一份,打开后改造并把程序模块化一下
main.c
MatrixLED.c
MatrixLED.h
然后接下来我们来写显示动画
比如我们想要显示一串字符“Hello!”然后让它向左流动。
那么我们先要存一个数组,这个数组存的是一长条的整幅动画,就是8*32或者更多。
接下来我们介绍一个辅助工具:文字取模软件
这个软件可以自动帮我们数据给提出来
首先要新建图像
填上宽度和高度32*8,点击确定后创建好一个区域
放大格点
参数设置,其他选项
纵向取模,确定
然后在这里我们可以手动地画一下我们要显示的东西
字幕画好了之后
然后我们先控制前八个显示,依次往后移动,然后字幕就能在屏幕上流动
取模方式,C51格式,下面就显示出来了我们的数据
直接把这个数据复制过来,放进我们定义好的数组里面
这个数组内每一个元素分别表示每一列的数据
然后我们需要定义数组元素的下标
然后用一个for循环让它循环流动显示每一列的数据
然后我们需要定义一个变量offset表示偏移量。
我们一开始一个for循环显示完8个元素(0~7号元素),也就是动画第一帧结束。
i+offset就让它从第二个元素开始显示起(1~8号元素),也就是动画的第二帧。每次+offset就从下一位循环起,如此反复每一次都把起始位置移一下,就形成了动画的很多帧。
然后我们还需要定义一个Count,每扫描一次后就+1,扫描10次的话相当于延时了(因为每一帧让它循环显示10遍,这样做的目的是让前一帧动画显示的时间长一点,为了避免下一帧动画显示时把前一帧动画给覆盖掉,显示10遍的话即使下一帧动画来覆盖也不可能完全覆盖掉前一帧动画)
Offset一直在加,当它加到头的时候溢出数组了,所以后面就产生乱码
为了防止数组溢出,再加一个if语句
但是这样写的话当它执行完所有帧的时候它会直接跳回到第一帧,这样显得比较突兀,我们可以在数组第一个元素前再加8个元素0x00(刚好等于一帧动画),然后最后一个元素后面也加8个元素0x00,这样就相当于第一帧动画前加一帧空白帧,最后一帧动画后面也加一帧空白帧,让最后一帧动画和第一帧动画能对接上,这样字幕播放就是流畅的,不会出现最后一帧动画播放完后立马又闪现出第一帧的动画的现象!
然后这里改成32
但是这样写还有一个问题就是播放最后一帧动画显示感叹号的时候,感觉突然消失了
我们可以再改成40,也就是让屏幕显示完40个数据(前五帧)后再对offset清零,这样就能让最后一个感叹号显示完之后显示一个空白帧然后再对offset清零,进入第二轮字幕播放。
最后主程序的代码如下:
效果请看视频:
用点阵屏做的流动字幕
最后补充
如果想要用点阵屏做一个逐帧动画也是可以的,可以自己琢磨在这个程序的基础上改。
还要说明一点:如果想要设置更长的动画的话,就在数组里面去更多的数据,就可以显示不同的动画。但是这个数组元素是否能无限加呢?不能!如果数据过多的时候,单片机的RAM就不够,因为我们这个数组的数据其实是存放在RAM里面的。
单片机有两种存储,一种是程序运行时的暂存器叫RAM,另一种是放在Flash里面的程序存储器(持续空间会大一些)。
而且这个数组里面的数据一般不需要改变其中的值,所以放在RAM里面会很浪费内存,所以我们一般把它放在flash里面。
怎么放在flash里面呢?需要在这里加一个关键字code,加上这个关键字后,数组里面的数据就会放在flash里面。
但是如果将这个数组放在code里面有个不好的地方就是这个数组不能更改了。如果尝试将数组中的某个元素重新赋值,程序就会报错。
以上就是本节的全部内容,源码已经放在评论区,如有问题可在评论区留言。