单片机8×8点阵显示简单汉字的程序_【Proteus】16乘16点阵滚动播放

602979568ef2d1a6ab59834a3e70118a.png

前言

16乘16点阵是比较常用的汉字显示工具,我们知道显示器都是由一个个点组成的,也可以理解成像素,显示器显示图案文字的原理与点阵并无多大区别,我们只需理解其中原理便可举一反三。因为英文字母及符号的显示最小单位为6乘8像素,就是说,要比较完整的显示英文字母,需要一个至少8行6列的矩形空间;而汉字则需要至少16乘16的空间才能比较完整的显示常用汉字。因此使用16乘16点阵驱动来滚动显示汉字是比较有学习价值的一个小课题。整个程序我都会附在最后,还是懒得复制粘贴,需要整个工程,仿真以及软件,16方点阵模型的就给我点赞赏吧。

先放一张最后的效果图:

97a7bd4d4b9a34f9ff73f61d891aca62.png

首先,我们需要找到proteus中的16乘16点阵,应该会发现proteus中只有8乘8的点阵,没有16乘16的,16乘16的点阵需要自己制作,或者就下载别人的,我已经把16乘16点阵的protues模型文件一起打包在资料里中,可以直接下载添加。按照模型的添加说明,把模型添加即可:

98e6e23c843b77bb3843d7ae6404acc4.png

然后,我们需要测试一下,这个16乘16点阵如何驱动。很简单,只需要一个VCC,一个GND,便可以测试。

首先我们需要测试出来这个16乘16点阵模型是左高右低亮灯还是,左低右高亮灯,测试如下:

b75822012600f4ed3eb4018ee928e6ba.png

可以看出这个点阵是左高右低亮灯,那么接下来我们需要测试出来,左边的这一列接口是控制列的还是控制行的,测试如下:

35cb5dff0d6d32ebfb719586670d9c64.png

由图可知,左边选中哪一行高电平,那么右边哪一行的最后一个就会亮,那么就可以看出,左边的这一列接口的功能是选中行,而右边的这一列接口的功能是选中列,从图中可以推测出,右边最上面的接口控制的是最后一个灯,最下面的接口控制的是从左到右第一个灯,那么接下来验证推测:

951bd8e0afc8f305dcad85a5d3488527.png

测试结果确实是这样,那么就明确了proteus中的这个仿真元件的驱动方法:左边的一列接口是选中行,右边的一列接口时选中列,只不过右边的接口从上往下接口对应的是每一行的从右至左,这一点要注意。

那么之后就可以把点阵接到单片机接口来编程测试,电路如图:

726e4b0dea689e527d95688efd594e50.png

电路上的标号,我按照点阵左右列的接口分别标注,左边的一列接口分别为L1-L16,右边的是R1-R16。那我们现在就测试编程,让第一行的最后一个亮,按照之前测试到的驱动方法,想要让第一行的最后一个亮,那也就是上图中的,L1给高,其余的L标号给低,右边R1给低,其余R标号给高,那么程序上就要给P2.7给1,P1.0给0。其余的L标号的给0,R标号的给1。

主程序如下:

7ff62d878facb8a00af7ed05d26a2d47.png

实验结果如同预想一样:

639b9ff80353a232af48e3a7a437c864.png

下一步,我们需要测试一下控制一列的点,例如,现在控制点阵最后一列除了最后4个,其他都亮。那么就需要R接口中R1变为低电平,其余为高电平,L系列接口除了L13-L16都置高。程序就应该改成:P3 = 0xff; P1 = 0xfe; P0 = 0xf0; P2 = 0xff;

结果如下:

83c310bf733a90e611cb9e18f029410c.png

那么我们现在就要理解一下点阵显示图案的原理,就是我们先显示第一列的点,然后把第一列的点清除,然后延时一段时间之后显示第二列的点,然后清除,然后延时一段时间之后显示第三列的点。我们按照这个思路,可以编写一个显示的驱动函数,用于后面的调用,简化程序。函数程序如下:

/*************************************************

驱动函数的参数含义在注释中也有详细说明,基本就是按照之前的思路来写的,选中某一列,然后在选中的列输入数据。

之后,我们就可以使用取模软件来进行汉字的取模,然后测试一下这个程序,显示一个汉字。

汉字取模软件我使用的是这个:(软件我已经一起打包)

bcffd25acb3777b3270340ea18e0203a.png

软件取模设置如下:

c4c2405165057b954347891d082ddd9a.png

阴码,逐列式,点阵16乘16,C51格式,顺相,行前缀不需要,行后缀只需要一个逗号即可。

程序如下:

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。这里数组取模的是“恭”字,实验结果如图:

8d396dedb1c8673ef3a2cf720ea1659d.png

然后我们就需要做出滚动显示的效果。滚动效果其实就是选中的列在变,也就是说:如果我们以扫描显示16列为一个周期的话,每过一个周期,就显示下一副画面就可以了,假如我们要显示“新年快乐!”这四个字还有个符号,那我们在取模软件中把这四个字加符号全部取模,然后再写入一个数组中,只需要推移显示这个数组中的数据即可,再简单易懂一点,“新年快乐!”一共是16行,16*5列数据,第一幅画面就是1-16列,第二幅画面就是2-17列,以此类推,我们需要使用一个定时器来控制推移画面的速度就可以了。

我们之前的主函数程序中对于显示是这样的:

display(str[t*2],str[t*2+1],t+1);

那么要想让这个显示数据推移,我们在t上加入一个flag就可以了,就像这样:

display(str[(t+flag)*2],str[(t+flag)*2+1],t+1);

我们在定时器中断中改变这个flag,让这个flag每次自加1,我们可以在定时器中控制flag自加1的时间,进而就可以控制滚动的速度了。

最终效果图如下:

cb385a753446c492037950afdbf62c2d.png

最终程序如下:

#include <reg52.h>

/***************************************************
点阵控制方式:按列操作,选中第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这个变量,如果滚动出现乱码,那么就说明数组的数据比较少,而这个变量比较大,后面就会出现乱码,调整这个变量避免出现乱码。

  • 23
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值