电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
【Proteus】16乘16点阵滚动播放
分 享
扫描二维码分享
【Proteus】16乘16点阵滚动播放
Proteus
点阵
单片机
KVIN
关注
发布时间: 2020-03-04
丨
阅读: 1141
# 前言 16乘16点阵是比较常用的汉字显示工具,我们知道显示器都是由一个个点组成的,也可以理解成像素,显示器显示图案文字的原理与点阵并无多大区别,我们只需理解其中原理便可举一反三。因为英文字母及符号的显示最小单位为6乘8像素,就是说,要比较完整的显示英文字母,需要一个至少8行6列的矩形空间;而汉字则需要至少16乘16的空间才能比较完整的显示常用汉字。因此使用16乘16点阵驱动来滚动显示汉字是比较有学习价值的一个小课题。 先放一张最后的效果图: ![](https://cf02.ickimg.com/bbsimages/202002/018f5c4504d9b5600d3aabc85603e7c0.png) 首先,我们需要找到proteus中的16乘16点阵,应该会发现proteus中只有8乘8的点阵,没有16乘16的,16乘16的点阵需要自己制作,或者就下载别人的,我已经把16乘16点阵的protues模型文件一起打包在资料里中,可以直接下载添加。按照模型的添加说明,把模型添加即可: ![](https://cf02.ickimg.com/bbsimages/202002/e8c2626247f60aa052382b003f2874d3.png) 然后,我们需要测试一下,这个16乘16点阵如何驱动。很简单,只需要一个VCC,一个GND,便可以测试。 首先我们需要测试出来这个16乘16点阵模型是左高右低亮灯还是,左低右高亮灯,测试如下: ![](https://cf02.ickimg.com/bbsimages/202002/1141d4f6e1b2eb50b4eb612049bfb441.png) 可以看出这个点阵是左高右低亮灯,那么接下来我们需要测试出来,左边的这一列接口是控制列的还是控制行的,测试如下: ![](https://cf02.ickimg.com/bbsimages/202002/36989f388505df8c156eb57d0ea71f13.png) 由图可知,左边选中哪一行高电平,那么右边哪一行的最后一个就会亮,那么就可以看出,左边的这一列接口的功能是选中行,而右边的这一列接口的功能是选中列,从图中可以推测出,右边最上面的接口控制的是最后一个灯,最下面的接口控制的是从左到右第一个灯,那么接下来验证推测: ![](https://cf02.ickimg.com/bbsimages/202002/8f25273e7668f5a9f5a1dada15d7fd20.png) 测试结果确实是这样,那么就明确了proteus中的这个仿真元件的驱动方法:左边的一列接口是选中行,右边的一列接口时选中列,只不过右边的接口从上往下接口对应的是每一行的从右至左,这一点要注意。 那么之后就可以把点阵接到单片机接口来编程测试,电路如图: ![](https://cf02.ickimg.com/bbsimages/202002/b457510b6538576b27dd55313cd00e7f.png) 电路上的标号,我按照点阵左右列的接口分别标注,左边的一列接口分别为L1-L16,右边的是R1-R16。那我们现在就测试编程,让第一行的最后一个亮,按照之前测试到的驱动方法,想要让第一行的最后一个亮,那也就是上图中的,L1给高,其余的L标号给低,右边R1给低,其余R标号给高,那么程序上就要给P2.7给1,P1.0给0。其余的L标号的给0,R标号的给1。 主程序如下: ![](https://cf02.ickimg.com/bbsimages/202002/161a88313cc74c1bab4c12651dae3cc5.png) 实验结果如同预想一样: ![](https://cf02.ickimg.com/bbsimages/202002/8a5dc9a2d21cb89ad92a2fde8def077f.png) 下一步,我们需要测试一下控制一列的点,例如,现在控制点阵最后一列除了最后4个,其他都亮。那么就需要R接口中R1变为低电平,其余为高电平,L系列接口除了L13-L16都置高。程序就应该改成:P3 = 0xff; P1 = 0xfe; P0 = 0xf0; P2 = 0xff; 结果如下: ![](https://cf02.ickimg.com/bbsimages/202002/a188a5a2fa95f7accbfb94c51fa73eff.png) 那么我们现在就要理解一下点阵显示图案的原理,就是我们先显示第一列的点,然后把第一列的点清除,然后延时一段时间之后显示第二列的点,然后清除,然后延时一段时间之后显示第三列的点。我们按照这个思路,可以编写一个显示的驱动函数,用于后面的调用,简化程序。函数程序如下: ```c /@@************************************************* 函数功能:点阵驱动函数,rowsbehind为每列数据前8位, rowsfollow为每列数据后8位,col为选中的列, P2为行前8,P0为行后8,P3为列前8,P1为列后8, 列高行低为亮。 *************************************************/ void display(unsigned char rowsbehind,unsigned char rowsfollow,int col) //显示函数,第一个参数为亮灯的某列中的哪几行,第二个参数为轮到哪列输入参数 { //一定要先给列选赋值,否则会出现数据混乱 if(col==1) //第1列 { P3=0x7f; P1=0xff; } else if(col==2) //第2列 { P3=0xbf; P1=0xff; } else if(col==3) //3 { P3=0xdf; P1=0xff; } else if(col==4) //4 { P3=0xef; P1=0xff; } else if(col==5) //5 { P3=0xf7; P1=0xff; } else if(col==6) //6 { P3=0xfb; P1=0xff; } else if(col==7) //7 { P3=0xfd; P1=0xff; } else if(col==8) //8 { P3=0xfe; P1=0xff; } else if(col==9) //9 { P3=0xff; P1=0x7f; } else if(col==10) //10 { P3=0xff; P1=0xbf; } else if(col==11) //11 { P3=0xff; P1=0xdf; } else if(col==12) //12 { P3=0xff; P1=0xef; } else if(col==13) //13 { P3=0xff; P1=0xf7; } else if(col==14) //14 { P3=0xff; P1=0xfb; } else if(col==15) //15 { P3=0xff; P1=0xfd; } else if(col==16) //16 { P3=0xff; P1=0xfe; } P2=rowsbehind; //前8位 P0=rowsfollow; //后8位 } ``` 驱动函数的参数含义在注释中也有详细说明,基本就是按照之前的思路来写的,选中某一列,然后在选中的列输入数据。 之后,我们就可以使用取模软件来进行汉字的取模,然后测试一下这个程序,显示一个汉字。 汉字取模软件我使用的是这个:(软件我已经一起打包上传资料区,文章后面会给出链接) ![](https://cf02.ickimg.com/bbsimages/202002/00fd8b632b9a5b5bdc5e11a8e60543d3.png) 软件取模设置如下: ![](https://cf02.ickimg.com/bbsimages/202002/222be45ded7918f9052d4bcf03e9369b.png) 阴码,逐列式,点阵16乘16,C51格式,顺相,行前缀不需要,行后缀只需要一个逗号即可。 程序如下: ```c code unsigned char str[]={ 0x04,0x20,0x04,0x20,0x24,0x44,0x24,0x98,0x25,0x02,0xFE,0x01,0x24,0xFE,0x24,0x00, 0x24,0x10,0xFE,0x0C,0x25,0x20,0x24,0x90,0x24,0x4C,0x04,0x20,0x04,0x20,0x00,0x00,/@@*"恭",0*/ }; /@@************************************************ 函数功能:延时函数 *************************************************/ void delay(int x) //延时函数 { int i = 0, j = 0; for( i = x; i > 0; i--) for( j = 5; j > 0; j--); } /@@************************************************* 函数功能:点阵驱动函数,rowsbehind为每列数据前8位, rowsfollow为每列数据后8位,col为选中的列, P2为行前8,P0为行后8,P3为列前8,P1为列后8, 列高行低为亮。 *************************************************/ void display(unsigned char rowsbehind,unsigned char rowsfollow,int col) //显示函数,第一个参数为亮灯的某列中的哪几行,第二个参数为轮到哪列输入参数 { //一定要先给列选赋值,否则会出现数据混乱 if(col==1) //第1列 { P3=0x7f; P1=0xff; } else if(col==2) //第2列 { P3=0xbf; P1=0xff; } else if(col==3) //3 { P3=0xdf; P1=0xff; } else if(col==4) //4 { P3=0xef; P1=0xff; } else if(col==5) //5 { P3=0xf7; P1=0xff; } else if(col==6) //6 { P3=0xfb; P1=0xff; } else if(col==7) //7 { P3=0xfd; P1=0xff; } else if(col==8) //8 { P3=0xfe; P1=0xff; } else if(col==9) //9 { P3=0xff; P1=0x7f; } else if(col==10) //10 { P3=0xff; P1=0xbf; } else if(col==11) //11 { P3=0xff; P1=0xdf; } else if(col==12) //12 { P3=0xff; P1=0xef; } else if(col==13) //13 { P3=0xff; P1=0xf7; } else if(col==14) //14 { P3=0xff; P1=0xfb; } else if(col==15) //15 { P3=0xff; P1=0xfd; } else if(col==16) //16 { P3=0xff; P1=0xfe; } P2=rowsbehind; //前8位 P0=rowsfollow; //后8位 } /@@********************************************************** 函数功能:先进行定时器初始化,然后进入while循环,一次显示一幅16*16 图像,因此,使用for循环,循环输入数据,显示16*16图像, 显示图像时delay(1)的时间是非常短的,可以忽略不计,不加的话 可能会出现数据混乱的状况,相比delay(1)的时间,定时器设定的 时间要长得多,每次移动一格。 display(str[t*2],str[t*2+1],t+1),由于每列数据需要两个16进制码, 因此需要乘2,t比行数多1,因此需要加1,要移动的话,将t变为 t+flag即可。 **********************************************************/ void main() { while(1) { int t = 0; for(t=0;t<16;t++) //一次显示16列,即一个屏幕 { display(str[t*2],str[t*2+1],t+1); //左移flag位 delay(1); } } } ``` 这里需要解释一下display函数第一个参数与第二个参数合成是一列的16位数据,第一个是前8位数据,第二个是后8位数据,数组存储数据是8位存储,因此才分开了两个参数,而且51单片机是8位端口操作,这样写比较适合51单片机。另外,t从0开始自加到15,因此代表的是选中的列数,数据是两个8位数据代表一列数据,因此需要乘2。这里数组取模的是“恭”字,实验结果如图: ![](https://cf02.ickimg.com/bbsimages/202002/c284c9fe9133976243a11523b3b791db.png) 然后我们就需要做出滚动显示的效果。滚动效果其实就是选中的列在变,也就是说:如果我们以扫描显示16列为一个周期的话,每过一个周期,就显示下一副画面就可以了,假如我们要显示“新年快乐!”这四个字还有个符号,那我们在取模软件中把这四个字加符号全部取模,然后再写入一个数组中,只需要推移显示这个数组中的数据即可,再简单易懂一点,“新年快乐!”一共是16行,16*5列数据,第一幅画面就是1-16列,第二幅画面就是2-17列,以此类推,我们需要使用一个定时器来控制推移画面的速度就可以了。 我们之前的主函数程序中对于显示是这样的: ```c display(str[t*2],str[t*2+1],t+1); ``` 那么要想让这个显示数据推移,我们在t上加入一个flag就可以了,就像这样: ```c display(str[(t+flag)*2],str[(t+flag)*2+1],t+1); ``` 我们在定时器中断中改变这个flag,让这个flag每次自加1,我们可以在定时器中控制flag自加1的时间,进而就可以控制滚动的速度了。 最终效果图如下: ![](https://cf02.ickimg.com/bbsimages/202002/e51eea886745b98d69365409a9477aef.gif) 整个程序工程,仿真工程,取模软件,以及16乘16点阵的proteus元件库我已经打包上传到下载区,懒癌同志可以帮忙给我贡献一点IC币,勤劳的同学继续往下看代码。下载链接如下:https://www.icxbk.com/download/detail/48654.html 最终程序如下: ```c #include
/@@*************************************************** 点阵控制方式:按列操作,选中第1列,输入16位数据,然后 第一列清除,选中第2列,输入16位数据,依次输入 16列数据,即为一幅16*16点阵图像,由于清除间隔 时间非常短,所以人眼分辨不出来。 使用定时器定时,控制字符及汉字移动速度,当触发 定时器时,判断是否到达设定值,若是,便左移一位, 以此实现滚动显示的效果。 ***************************************************/ //取模数组 code unsigned char str[]={ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//之前都是黑屏 0x02,0x04,0x22,0x48,0x2A,0x52,0xA6,0x41,0x63,0xFE,0x26,0x40,0x2A,0x50,0x22,0x49, 0x00,0x06,0x3F,0xF8,0x22,0x00,0x22,0x00,0x23,0xFF,0x42,0x00,0x02,0x00,0x00,0x00,/@@*"新",0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/@@*" ",1*/ 0x00,0x20,0x04,0x20,0x18,0x20,0xE3,0xE0,0x22,0x20,0x22,0x20,0x22,0x20,0x22,0x20, 0x3F,0xFF,0x22,0x20,0x22,0x20,0x22,0x20,0x22,0x20,0x20,0x20,0x00,0x20,0x00,0x00,/@@*"年",2*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/@@*" ",3*/ 0x00,0x80,0x07,0x00,0x00,0x00,0xFF,0xFF,0x08,0x00,0x04,0x81,0x10,0x82,0x10,0x8C, 0x10,0xB0,0xFF,0xC0,0x10,0xB0,0x10,0x8C,0x1F,0x82,0x00,0x81,0x00,0x81,0x00,0x00,/@@*"快",4*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/@@*" ",5*/ 0x00,0x00,0x00,0x04,0x07,0x08,0x39,0x10,0x21,0x60,0x21,0x02,0x21,0x01,0x2F,0xFE, 0x41,0x00,0x41,0x00,0xC1,0x40,0x41,0x20,0x01,0x10,0x01,0x0C,0x00,0x00,0x00,0x00,/@@*"乐",6*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x38,0x00,0x7F,0xCC, 0x7F,0xCC,0x38,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //! 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //空白 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //空白 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 //空白 }; //二 int time = 0; //计时参数 int flag = 0; //移动参数 int num = 0; //移动次数计数 int V = 8; //移动速度,越小越快 int clear_counter = 120; //清零计数 当移动次数大于此变量时,重新开始 /@@************************************************ 函数功能:延时函数 *************************************************/ void delay(int x) //延时函数 { int i = 0, j = 0; for( i = x; i > 0; i--) for( j = 5; j > 0; j--); } /@@************************************************* 函数功能:点阵驱动函数,rowsbehind为每列数据前8位, rowsfollow为每列数据后8位,col为选中的列, P2为行前8,P0为行后8,P3为列前8,P1为列后8, 列高行低为亮。 *************************************************/ void display(unsigned char rowsbehind,unsigned char rowsfollow,int col) //显示函数,第一个参数为亮灯的某列中的哪几行,第二个参数为轮到哪列输入参数 { //一定要先给列选赋值,否则会出现数据混乱 if(col==1) //第1列 { P3=0x7f; P1=0xff; } else if(col==2) //第2列 { P3=0xbf; P1=0xff; } else if(col==3) //3 { P3=0xdf; P1=0xff; } else if(col==4) //4 { P3=0xef; P1=0xff; } else if(col==5) //5 { P3=0xf7; P1=0xff; } else if(col==6) //6 { P3=0xfb; P1=0xff; } else if(col==7) //7 { P3=0xfd; P1=0xff; } else if(col==8) //8 { P3=0xfe; P1=0xff; } else if(col==9) //9 { P3=0xff; P1=0x7f; } else if(col==10) //10 { P3=0xff; P1=0xbf; } else if(col==11) //11 { P3=0xff; P1=0xdf; } else if(col==12) //12 { P3=0xff; P1=0xef; } else if(col==13) //13 { P3=0xff; P1=0xf7; } else if(col==14) //14 { P3=0xff; P1=0xfb; } else if(col==15) //15 { P3=0xff; P1=0xfd; } else if(col==16) //16 { P3=0xff; P1=0xfe; } P2=rowsbehind; //前8位 P0=rowsfollow; //后8位 } void timer0_init() //timer0初始化 { TMOD=0x01; //定时器0的工作方式1 TH0=(65535-10000)%256; //赋初值 TL0=(65535-10000)/256; TR0=1; //开定时器0 ET0=1; //开定时器0中断 EA=1; //开总中断 } /@@********************************************************** 函数功能:先进行定时器初始化,然后进入while循环,一次显示一幅16*16 图像,因此,使用for循环,循环输入数据,显示16*16图像, 显示图像时delay(1)的时间是非常短的,可以忽略不计,不加的话 可能会出现数据混乱的状况,相比delay(1)的时间,定时器设定的 时间要长得多,每次移动一格。 display(str[t*2],str[t*2+1],t+1),由于每列数据需要两个16进制码, 因此需要乘2,t比行数多1,因此需要加1,要移动的话,将t变为 t+flag即可。 **********************************************************/ void main() { timer0_init(); //定时器初始化函数 while(1) { int t = 0; for(t=0;t<16;t++) //一次显示16列,即一个屏幕 { display(str[(t+flag)*2],str[(t+flag)*2+1],t+1); //左移flag位 delay(1); } } } /@@********************************************** 函数功能:定时器中断函数,10ms触发一次,触发一次, time自加,代表10ms时间,V为设定时间,用来 控制滚动速度,V越大,滚动速度越慢, flag为每次移动格数,flag为1,即每次移动1格, 可以使用仿真文件看一下具体效果。每次滚动便将 time清零。 ***********************************************/ void timer0_interrupt() interrupt 1 { TH0=(65535-10000)%256; //10ms TL0=(65535-10000)/256; time++; if(time==V) //控制移动的速率 { num++; flag = flag + 1; //每次移动一格 if(num > clear_counter) { num=0; flag=0; } time=0; //清零 } } ``` 注意这几个变量的作用,关于clear_counter这个变量,如果滚动出现乱码,那么就说明数组的数据比较少,而这个变量比较大,后面就会出现乱码,调整这个变量避免出现乱码。
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
2
)
KVIN
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交