點陣的移位一般有上、下、左、右的移動,這里我們重點講上移和左移,其它同理。
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漢字的編碼個數。這樣就完成了整個點陣左移的控制(這里使用了串口實現點陣的左移,當然我們也可以不用串口,關于非串口實現的左移后面介紹),它的過程比較復雜,需反復思考。