一.概述
點陣式液晶顯示模塊(LCD)廣泛應用於單片機控制系統,比數碼管、段式液晶能模塊顯示更多、更直觀的信息,如漢字、曲線、圖片等。點陣液晶顯示模塊集成度很高,一般都內置控制芯片、行驅動芯片和列驅動芯片,點陣數量較大的LCD還配置RAM芯片,帶漢字庫的LCD還內嵌漢字庫芯片,有負壓輸出的LCD還設有負壓驅動電路等。單片機讀寫LCD實際上就是對LCD的控制芯片進行讀寫命令和數據。編程驅動LCD時,不需要對LCD的結構和點陣行列驅動原理深入了解,只要理解LCD接口的定義和LCD的控制芯片的讀寫時序和命令就可以了。
12864LCD屬於點陣圖形液晶顯示模塊,不但能顯示字符,還能顯示漢字和圖形,分帶漢字庫和不帶那字庫兩種,價格也有差別。帶漢字庫的12864LCD使用起來非常方便,不需要編寫復雜的漢字顯示程序,只要按時序寫入兩個字節的漢字機內碼,漢字就能顯示出來了,驅動程序簡單許多。本文例程就是基於帶漢字庫的12864液晶顯示模塊。
常見的12864LCD使用的控制芯片是ST7920。ST7920一般和ST7921(列驅動芯片)配合使用,做成顯示2行每行16個漢字的顯示屏25632LCD,或者是做成4行每行8個漢字的顯示屏12864LCD。12864LCD的讀寫時序和1602LCD是一樣的,完全可以照搬1602LCD驅動程序的讀寫函數。需要注意的是,12864LCD分成上半屏和下半屏,而且兩半屏之間的點陣內存映射地址不連續,給驅動程序的圖片顯示函數的編寫增加了難度。
二.電路設計
通常,12864LCD屏內都會自帶背光限流電阻,背光管腳可以直接連接DC5V電源使用。12864LCD對比度調節的電位器取值10K-20K歐姆。如果說明書有明確要求的,請使用說明書規定的參數。
三.軟件設計
/*****************************************************************
*程序名稱:帶漢字庫的12864液晶顯示模塊驅動
*程序功能:顯示字符、漢字和圖片
*開發工具:WSF-51DB開發板
* MCU型號:AT89S52-24PU
*時鍾頻率:12MHZ
*程序作者:吳師傅
*版權說明:吳師傅版權所有。轉載請注明來源和作者
*****************************************************************/
#i nclude<reg52.h>
sbit E=P3^4;//脈沖使能
sbit RW=P3^3;//讀寫選擇
sbit RS=P3^2;//數據命令選擇
sbit rst=P1^0;//12864復位
unsigned char code pictrue2[192]={//圖片:機器貓48*32
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0X80,0X00,0X00,0X00,0X1F,0XFF,
0XFE,0X00,0X00,0X00,0X7F,0X1F,0XFF,0X80,0X00,0X01,0XFC,0X0C,0X3F,0XE0,0X00,0X07,
0XF8,0X48,0X1F,0XF8,0X00,0X0F,0X98,0XD1,0X8F,0XFC,0X00,0X1C,0X4C,0XF3,0X9F,0XFE,
0X00,0X1A,0X33,0X39,0XB3,0XFE,0X00,0X33,0XE3,0XFC,0X40,0XFE,0X00,0X31,0X81,0XE1,
0X9E,0X7F,0X00,0X10,0XC1,0X03,0XC0,0X3E,0X00,0X18,0X01,0X03,0X70,0X9E,0X00,0X1F,
0X03,0X03,0X83,0X9E,0X00,0X09,0XE2,0X00,0X3E,0X1C,0X00,0X04,0X3F,0X80,0XFF,0X3C,
0X00,0X07,0X01,0XFF,0XC0,0X70,0X00,0X0F,0XC0,0X00,0X00,0XE0,0X00,0X1F,0XF8,0X00,
0X03,0X80,0X0F,0X0C,0X3F,0XE0,0X1E,0X00,0X39,0XED,0X3B,0XFF,0XFF,0X00,0X20,0X7A,
0XFE,0X07,0XFF,0X80,0X60,0X1C,0X1C,0X07,0XFF,0X80,0X60,0X0F,0X03,0XF7,0XFF,0X80,
0X20,0X0F,0XDE,0X3F,0XFF,0X80,0X30,0X0F,0XF0,0X0F,0XFF,0XA0,0X1C,0X0F,0XE0,0X07,
0XF8,0X30,0X0F,0XFF,0XE0,0X03,0XC8,0X60,0X01,0XE0,0X20,0X03,0X87,0XC0,0X00,0X00,
0X30,0X07,0X00,0X00,0X00,0X00,0X0C,0X0C,0X00,0X00,0X00,0X00,0X07,0XF0,0X00,0X00,
};
// 延時ms函數:
void Delayms(unsigned int t)
{
unsigned int i,j;
for(i=t;i>0;i--)
for(j=0;j<120;j++);
}
// 12864檢查狀態函數:
void Check12864State(void)
{
P2=0xff;
E=0;//讀狀態前三控制線的狀態
RS=0;
RW=1;
E=1;//拉高,讀狀態
while((P2&0x80)==0x80);//等待空閑
E=0;//寫命令后三控制線的狀態
RS=1;
RW=0;
}
// 12864寫命令函數:
void Write12864Command( unsigned char com)
{
Check12864State();//檢查狀態
P2=com;//賦值
E=0;//寫命令前三控制線的狀態
RS=0;
RW=0;
E=1;//拉高,寫命令
E=0;//寫命令后三控制線的狀態
RS=1;
RW=1;
}
//12864寫數據函數:
void Write12864Data( unsigned char dat)
{
Check12864State();//檢查狀態
P2=dat;//賦值
E=0;//寫數據前三控制線的狀態
RS=1;
RW=0;
E=1;//拉高,寫數據
E=0;//寫數據后三控制線的狀態
RS=0;
RW=1;
}
//在指定的位置顯示變量值函數:
void LCD12864DisplayValue( unsigned char y,unsigned char x,unsigned char v)
//y-行數值0-3,x-列數值0-7,v-變量
{
unsigned char row;
Write12864Command(0x30);//基本指令
Write12864Command(0x06);//地址計數器自動加以,光標右移
switch(y)//根據行號選擇行地址
{
case 0:row=0x80;break;//第一行首地址
case 1:row=0x90;break;//第二行首地址
case 2:row=0x88;break;//第三行首地址
case 3:row=0x98;break;//第四行首地址
default:;
}
Write12864Command(row+x);//寫地址
Write12864Data(0x30+v/100);//百位,先將數字化成ASCII碼,再寫入
Write12864Data(0x30+v%100/10);/十位/先將數字化成ASCII碼,再寫入
Write12864Data(0x30+v%10);//個位,先將數字化成ASCII碼,再寫入
}
//在指定的位置顯示字符串(漢字和ASCII碼字符)函數:
void LCD12864DisplayString( unsigned char y,unsigned char x, unsigned char *pstr)
//y-行數值0-3,x-列數值0-7,pstr-字符串指針
//12864可以顯示32個漢字(四行每行8個),一個地址對應一個漢字
//可以顯示64個ASCII碼字符(四行每行16個),一個地址對應兩個字符
//為了實現自動換行功能,這個函數比較繁瑣
{
unsigned char row,n=0;
Write12864Command(0x30);//基本指令
Write12864Command(0x06);//地址計數器自動加以,光標右移
switch(y)//根據行號選擇行地址
{
case 0:row=0x80;break;//第一行首地址
case 1:row=0x90;break;//第二行首地址
case 2:row=0x88;break;//第三行首地址
case 3:row=0x98;break;//第四行首地址
default:;
}
Write12864Command(row+x);//寫地址
while(*pstr!='\0')
{
Write12864Data(*pstr);//寫字符
pstr++;
n++;//計數
if((n+x*2)==16)//如果一行寫完,繼續寫第二行
{
if(y==0) Write12864Command(0x90);//寫下一行地址
else if(y==1) Write12864Command(0x88);//寫下一行地址
else if(y==2) Write12864Command(0x98);//寫下一行地址
else ;
}
else if((n+x*2)==32)//如果第二行寫完,繼續寫第三行
{
if(y==0) Write12864Command(0x88);//寫下一行地址
else if(y==1) Write12864Command(0x98);//寫下一行地址
else ;
}
else if((n+x*2)==48)//如果第三行寫完,繼續寫第四行
{
if(y==0) Write12864Command(0x98);//寫下一行地址
else ;
}
else ;
}
}
//圖片模式清屏函數:
void Clear12864Screen()
{
unsigned char i,j;
Write12864Command(0x34);//功能設定:8位控制方式,使用擴充指令
Write12864Command(0x36);//使用擴充指令,繪圖顯示控制
for(i=0;i<32;i++)
//ST7920可控制256*32點陣(32行256列),而12864液晶實際的行地址只有0-31行,
//12864液晶的32-63行的行是0-31行地址從第128列划分一半出來的,所以分為上下兩半屏,
//也就是說第0行和第32行同屬一行,行地址相同;第1行和第33行同屬一行,以此類推
{
Write12864Command(0x80|i);//寫行地址(垂直地址)
Write12864Command(0x80);//寫列地址(水平地址)
for(j=0;j<32;j++)
Write12864Data(0x00);//清屏
}
}
//在任意位置顯示任意大小的圖片函數:
void LCD12864DisplayPictrue(unsigned char y,unsigned char x,
unsigned char px,unsigned char py, unsigned char *pp)
//y-起始行(數值0-63),x-起始列(16位寬,數值0-7),
//px-圖片寬度,py-圖片高度,pp-指針指向圖片數組
//因為上下屏的地址不連續,要在任意位置顯示完整的圖像,處理起來比較繁瑣
{
unsigned char i,j,k;
Clear12864Screen();//清屏
if(y<32)//如果起始行在上半屏
{
k=32-y;//算出上半屏的行數
for(i=0;i<k;i++,y++)//上半屏行數
{
Write12864Command(0x80|y);//寫行地址(垂直地址)
Write12864Command(0x80|x);//寫列地址(水平地址)
for(j=0;j<px/8;j++)
Write12864Data(pp[i*px/8+j]);//寫圖片數據
}
y=0;//下半屏起始行,接上半屏繼續寫數據
for(;i<py;i++,y++)//下半屏剩下的行數
{
Write12864Command(0x80|y);//寫行地址(垂直地址)
Write12864Command(0x80|(8+x));//寫列地址(水平地址)
for(j=0;j<px/8;j++)
Write12864Data(pp[i*px/8+j]);//寫圖片數據
}
}
else //如果起始行在下半屏
{
for(i=0;i<py;i++,y++)//行數
{
Write12864Command(0x80|(y-32));//寫行地址(垂直地址)
Write12864Command(0x80|(8+x));//寫列地址(水平地址)
for(j=0;j<px/8;j++)
Write12864Data(pp[i*px/8+j]);//寫圖片數據
}
}
}
//12864初始化函數:
void Initialize12864()
{
rst=0;//復位12864
Delayms(30);
rst=1;
Delayms(20);
Write12864Command(0x30);//功能設定:8位控制方式,使用基本指令
Write12864Command(0x08);//顯示關
Write12864Command(0x01);//清屏
Write12864Command(0x06);//地址計數器加一、光標右移
Write12864Command(0x0c);//顯示開
}
//主函數:
int main(void)
{
Initialize12864();
LCD12864DisplayString(0,0,"歡迎您來到吳師傅博客學習和交流! wellcom!");
//顯示漢字和字符
LCD12864DisplayPictrue(32,4,48,32,pictrue2);//顯示機器貓
LCD12864DisplayValue(3,0,255);//顯示255
while(1);
return 0;
}