点阵的移位一般有上、下、左、右的移动,这里我们重点讲上移和左移,其它同理。
1. 点阵的上移:
点阵的上移相对来说很简单,看效果图如下:
源代码:(该程序实现了循环上移显示“邢台”)
/************16*16LED点阵屏显示*****************/
#include<reg52.h>
sbit R="P2"^0; //数据输入端口
sbit CLK="P2"^1; // 时钟信号
sbit STB="P2"^2; // 锁存端
char code table[]={
/*-- 文字: 邢 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x00,0x00,0xFE,0x3E,0x48,0x22,0x48,0x22,
0x48,0x12,0x48,0x12,0x48,0x0A,0xFF,0x13,
0x48,0x22,0x48,0x42,0x48,0x42,0x48,0x46,
0x44,0x2A,0x44,0x12,0x42,0x02,0x40,0x02,
/*-- 文字: 台 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x40,0x00,0x40,0x00,0x20,0x00,0x10,0x04,
0x08,0x08,0x04,0x10,0xFE,0x3F,0x00,0x20,
0x00,0x08,0xF8,0x1F,0x08,0x08,0x08,0x08,
0x08,0x08,0x08,0x08,0xF8,0x0F,0x08,0x08,
};
void delay(int z)
{
int x,y;
for(x=0;x<z;x++)
for(y=0;y<110;y++);
}
void WriteByte(char dat) //写一个字节的数据
{
char i;
for(i=0;i<8;i++) //循环8次把编码传给锁存器
{
dat=dat>>1; //右移一位,取出该字节的最低位
R=CY; //将该字节的最低位传给R
CLK=0; //将数据送出,上升沿
CLK=1;
}
}
void main()
{
int num,move,speed;
while(1)
{
if(++speed>8) //移动速度控制
{
speed=0;
move++; //移位
if(move>16) //是否完成移位一个汉字
move=0; //从头开始
}
for(num=0;num<16;num++)
{
WriteByte(table[2*num+move*2]); //送出一个字节
WriteByte(table[2*num+1+move*2]);
P1=num; //行选
STB=1; //输出锁存器中的数据,下降沿
STB=0;
delay(2);
}
}
}
可以看到这个程序和静态显示的程序没有太大的差距,主要就是加入了一个move变量来控制移动,WriteByte(table[2*num+move*2])中当move变量变化的时候更改了写入595中的数据,正好实现了移动显示的效果。而speed变量的if判断语句能够控制移动速度的大小。下面重点讲左移。
2. 点阵的左移:
因为点阵的数据最终是一个一个字节的并行送出的,所以要实现点阵的左移,我们就需要考虑如何才能够动态的更改每一个发送字节的数据,而汉字的每一个字节的编码是固定的,这里我们可以使用一个数据缓冲区来完成点阵的左移。重点说一下点阵左移中关键的一步操作temp=(BUFF>>tempyid) | (BUFF[s+1]<<(8-tempyid))。这里temp作为要发送的一个字节数据,它由数据缓冲区中的数据组合而成,并且动态的变化,大致来说就是首先第一个字节的数据右移tempyid位,第二个字节的数据左移8-tempyid位,两者相或后组成一个字节新的数据,只要我们一直不断地移位、相或、发送,就能实现左移的效果。不太好理解,先来看实例(循环左移显示“邢台学院”),效果图如下:
见源代码:
#include <AT89x51.H>
#define uchar unsigned char
#define uint unsigned int
uchar yid,h; //YID为移动计数器,H为行段计数器
uint zimuo; //字模计数器
uchar code hanzi[]; //汉字字模
uchar BUFF[4]; //缓存
void in_data(void); //调整数据
void rxd_data(void); //发送数据
void sbuf_out(); //16段扫描
uchar code table[]={//篇幅有限,省略编码};
void main(void)
{
uchar i,d=10;
yid=0;
zimuo=0;
while(1)
{
while(yid<16) //数据移位。
{
for(i=0;i<d;i++) //移动速度
{
sbuf_out();
}
yid++; //移动一步
}
yid=0;
zimuo=zimuo+32; //后移一个字,
if(zimuo>=96) //到最后从头开始,有字数决定
zimuo=0;
}
}
/********************************/
void sbuf_out()
{
for(h=0;h<16;h++) //16行扫描
{
in_data(); //调整数据
rxd_data(); //串口发送数据
P1=0x7f; //关闭显示。
P1_7=1; //锁存为高,595锁存信号
P1=h; //送行选
}
}
/******************************************************/
void in_data(void)
{
char s;
for(s=1;s>=0;s--) //h为向后先择字节计数器,zimuoo为向后选字计数器
{
BUFF[2*s+1]=table[zimuo+1+32*s+2*h]; //把第一个字模的第一个字节放入BUFF0
//中,第二个字模的第一个字节放入BUFF2中
BUFF[2*s]=table[zimuo+32*s+2*h]; // 把第一个字模的第二个字节放入BUFF1中,
//第二个字模的第二个字节放入BUFF3中
}
}
/*******************************************************/
void rxd_data(void) //串行发送数据
{
char s;
uchar inc,tempyid,temp;
if(yid<8)
inc=0;
else
inc=1;
for(s=0+inc;s<2+inc;s++) //发送2字节数据
{
if(yid<8)
tempyid=yid;
else
tempyid=yid-8;
temp=(BUFF>>tempyid)|(BUFF[s+1]<<(8-tempyid));//h1左移tempyid位后和h2右移8-tempyid相或,取出移位后的数据
SBUF=temp;//把BUFF中的字节从大到小移位相或后发送输出。
while(!TI); //注:这里使用了串口,串口数据的发送为最低位在前。
TI=0; //等待发送中断
}
}
首先来看定义的数据缓冲区BUFF[ ],这里一开始将会存储第一个汉字与第二个汉字的第一行的编码,该缓冲区动态的存储点阵屏每一行要发送的数据,注意这里BUFF的大小为4个字节,比16*16点阵屏要显示的汉字多了一个汉字行的大小,这一点是必要的,这样我们才能实现利用该缓冲区进行左移控制,接着来看in_data(void)函数,利用该函数,我们实现了动态的修改缓冲区中的数据,这里不再详述过程,重点看程序的注释即可。然后看rxd_data(void)函数,该函数的作用正是利用串口串行发送数据,也就是上面提到的移位、相或然后发送,关于在移位过程中的具体实现细节以及如何协调的进行数据发送,首先来看inc变量,该变量决定了从BUFF缓冲区中的第一个还是第二个数据开始读取,当移位开始后,在移完一个字节的数据之前我们都从BUFF数据缓冲区中的第一个字节开始读取,当移完一个字节后,inc变成1,这时我们从BUFF数据缓冲区中的第二个字节开始读取,于此同时后一个字节总是在和前一个字节的数据进行移位相或,达到慢慢向前推进的效果,这里有一个临界点,就是当移位满16位后,即一个汉字移出点阵屏后,这时候我们就需要将数据缓冲区中的数据进行更新,即后移一个字,这时数据缓冲区中的数据就变成了第二个汉字和第三个汉字的第一行汉字的编码,以此类推。下面来看sbuf_out()函数,该函数实现了16行的扫描,最后来看while循环内,这时主函数内已经很简单了,首先在while(yid<16)内,有控制移动速度的for循环,即显示几次静态的画面移动一步,而zimuo变量为移位过程中汉字的选择变量,它每32位的变化,正好是一个16*16汉字的编码个数。这样就完成了整个点阵左移的控制(这里使用了串口实现点阵的左移,当然我们也可以不用串口,关于非串口实现的左移后面介绍),它的过程比较复杂,需反复思考。