一、關(guān)于LCD1602:
在編寫LCD1602程序前,我們必須了解其手冊上一些非常重要的信息,如果這些信息不能理解透徹,編程可能會(huì)遇到或多或少的問題,在此先大致歸納幾點(diǎn)。
1.管腳:
1602共16個(gè)管腳,但是編程用到的主要管腳不過三個(gè),分別為:RS(數(shù)據(jù)命令選擇端),R/W(讀寫選擇端),E(使能信號(hào));以后編程便主要圍繞這三個(gè)管腳展開進(jìn)行初始化,寫命令,寫數(shù)據(jù)。
以下具體闡述這三個(gè)管腳:
RS為寄存器選擇,高電平選擇數(shù)據(jù)寄存器,低電平選擇指令寄存器。
R/W為讀寫選擇,高電平進(jìn)行讀操作,低電平進(jìn)行寫操作。
E端為使能端,后面和時(shí)序聯(lián)系在一起。
除此外,D0~D7分別為8位雙向數(shù)據(jù)線。
RS
|
R/W
|
操作說明
|
0
|
0
|
寫入指令碼D0~D7
|
0
|
1
|
讀取輸出的D0~D7狀態(tài)字
|
1
|
0
|
寫入數(shù)據(jù)D0~D7
|
1
|
1
|
從D0~D7讀取數(shù)據(jù)
|
注:關(guān)于E=H脈沖——開始時(shí)初始化E為0,然后置E為1,再清0.
讀取狀態(tài)字時(shí),注意D7位,D7=1,禁止讀寫操作;D7=0,允許讀寫操作;
所以對控制器每次進(jìn)行讀寫操作前,必須進(jìn)行讀寫檢測。(即后面的讀忙子程序)
3.指令集:
LCD_1602 初始化指令小結(jié):
0x38 設(shè)置16*2顯示,5*7點(diǎn)陣,8位數(shù)據(jù)接口
0x01 清屏
0x0F 開顯示,顯示光標(biāo),光標(biāo)閃爍
0x08 只開顯示
0x0e 開顯示,顯示光標(biāo),光標(biāo)不閃爍
0x0c 開顯示,不顯示光標(biāo)
0x06 地址加1,當(dāng)寫入數(shù)據(jù)的時(shí)候光標(biāo)右移
0x02 地址計(jì)數(shù)器AC=0;(此時(shí)地址為0x80) 光標(biāo)歸原點(diǎn),但是DDRAM中斷內(nèi)容不變
0x18 光標(biāo)和顯示一起向左移動(dòng)
4.顯示地址:
LCD1602內(nèi)部RAM顯示緩沖區(qū)地址的映射圖,00~0F、40~4F分別對應(yīng)LCD1602的上下兩行的每一個(gè)字符,只要往對應(yīng)的RAM地址寫入要顯示字符的ASCII代碼,就可以顯示出來。
5.讀寫時(shí)序:
時(shí)序圖1602手冊中有,這里不引用了。時(shí)序圖很重要,編程就是根據(jù)時(shí)序圖設(shè)置寄存器,讓LCD工作。
二、LCD1602程序編寫流程:
LCD1602在了解完以上信息后便可以編寫,這里我們把程序分為以下幾步:
1.定義LCD1602管腳,包括RS,R/W,E。這里定義是指這些管腳分別接在單片機(jī)哪些I/O口上。
現(xiàn)舉例如下:
sbit EN=P3^4;
sbit RS=P3^5;
sbit RW=P3^6;
2.顯示初始化,在這一步進(jìn)行初始化及設(shè)置顯示模式等操作,包括以下步驟:
設(shè)置顯示方式
延時(shí)
清理顯示緩存
設(shè)置顯示模式
通常推薦的初始化過程如下:
延時(shí)15ms
寫指令38H
延時(shí)5ms
寫指令38H
延時(shí)5ms
寫指令38H
延時(shí)5ms
注:以上寫38H指令可以看情況省略1~2步
(以上都不檢測忙信號(hào))
(以下都要檢測忙信號(hào))
寫指令38H
寫指令08H 關(guān)閉顯示
寫指令01H 顯示清屏
寫指令06H 光標(biāo)移動(dòng)設(shè)置
寫指令0cH 顯示開及光標(biāo)設(shè)置
3.設(shè)置顯示地址(寫顯示字符的位置)。
4.寫顯示字符的數(shù)據(jù)。
三、LCD1602各子程序模塊及主程序編寫:
現(xiàn)在按照上面編寫程序的流程,給出各子程序模塊及主程序的例子。
1.頭文件,宏定義,定義管腳等:
#include<reg52.h>
#include <string.h>
#define uchar unsigned char
#define uint unsigned int
sbit EN=P3^4;
sbit RS=P3^5;
sbit RW=P3^6;
uchar code table0[]={"QQ:545699636"}; //此條語句為顯示字符串時(shí)定義的字符串?dāng)?shù)組
2.LCD1602基本初始化子程序:
void LCD1602()
{
EN=0;
RS=1;
RW=1;
P0=0xff; //這里P0為與LCD D0~D7相連的I/O口
}
3.讀忙子程序:
void read_busy()
{
P0=0xff;
RS=0;
RW=1;
EN=1;
while(P0&0x80); //P0和10000000相與,D7位若不為0,停在此處
EN=0; //若為0跳出進(jìn)入下一步;這條語句的作用就是檢測D7位
} //若忙在此等待,不忙跳出讀忙子程序執(zhí)行讀寫指令
4.寫指令寫數(shù)據(jù)子程序:
void write(uchar i,bit j)
{
read_busy();
P0=i; //其中i=0,寫指令;i=1,寫數(shù)據(jù);
RS=j;
RW=0;
EN=1;
EN=0;
}
5.延時(shí)子程序:
void delay(uint c) //功能為提供初始化等其他子程序中的延時(shí)1xc MS
{
uint a,b;
for(a=0;a<c;a++)
for(b=0;b<120;b++);
}
6.LCD1602初始化子程序:
void init() //完全按照要求初始化流程來,中間省略了一步寫指令38H
{
delay(15);
write(0x38,0);
delay(5);
write(0x38,0);
write(0x08,0);
write(0x01,0);
write(0x06,0);
write(0x0c,0);
}
7.顯示單個(gè)字符子程序:
void display_lcd_byte(uchar y,uchar x,uchar z) //Y=0,1(起始行)X=0~15(起始列)Z=想寫字符的ASCII碼
{
if(y) //是否顯示在第二行(若在第一行Y=0,不進(jìn)入IF語句,若在第二行,進(jìn)入IF語句
{
x+=0x40; //第二行起始地址加上列數(shù)為字符顯示地址
}
x+=0x80; //設(shè)置數(shù)據(jù)指針位置
write(x,0);
write(z,1); //寫入數(shù)據(jù)
}
8.顯示字符串子程序:
void display_lcd_text(uchar y,uchar x,uchar table[]) //Y,X同上字符顯示,table[]字符串?dāng)?shù)組
{
uchar z=0;
uchar t;
t=strlen(table)+x; // 求得字符串長度加上起始列位置
while(x<t) //功能為LCD顯示到字符串最后一個(gè)字符,防止字符串
{ //沒有16個(gè)字符,從而不夠位產(chǎn)生亂碼;
display_lcd_byte(y,x,table[z]); //逐位顯示數(shù)組內(nèi)字符
x++;
z++;
}
}
9.主程序:
主程序里除了放入初始化程序外就是加入自己編寫的顯示子程序,根據(jù)你所要的不用功能可以編寫各種類型的顯示子程序,這里不做詳細(xì)介紹,以下舉例為顯示一個(gè)字符和顯示字符串的顯示子程序。
void main()
{
LCD1602();
init();
display_lcd_byte(0,0,'A'); // 顯示一個(gè)字符
display_lcd_text(1,3,table); // 顯示字符串
while(1);
}
到此,讓LCD1602顯示的操作流程和編程思想基本可以告一段落了,這里有一個(gè)完整的顯示程序可顯示2行英文: http://www.torrancerestoration.com/mcu/723.html,大家可依樣畫瓢,自己修改運(yùn)行, 但是1602的功能實(shí)現(xiàn)遠(yuǎn)不止這些。利用1602你可以做出動(dòng)態(tài)效果的顯示,并且除了顯示一般字符外,1602還支持自定義字符等等其他一些功能,下面在最后簡單介紹下顯示動(dòng)態(tài)效果和自定義字符。
一、顯示動(dòng)態(tài)效果:
顯示動(dòng)態(tài)效果包括讓一個(gè)字符或字符串原位置閃爍,或者前后移動(dòng)等等。其實(shí)動(dòng)態(tài)效果原理很簡單,就是簡單的利用延時(shí)。
例如讓字符原位置閃爍,可以認(rèn)為是先讓1602顯示字符,延時(shí)一段時(shí)間后,可以顯示空格或者直接清屏操作都可以達(dá)到讓字符消失不見的效果,再延時(shí)一段時(shí)間后再讓1602顯示這個(gè)字符。
同理,讓字符前后移動(dòng)也是這樣,例如讓字符在第一個(gè)位置顯示,延時(shí)一段時(shí)間后讓其在后面第二個(gè)位置顯示,只要顯示地址加1,然后顯示即可。字符串也是同樣的道理。
在這里補(bǔ)充一點(diǎn)就是如何讓字符串從1602第16個(gè)地址外進(jìn)入,動(dòng)態(tài)向前移動(dòng)。其實(shí)可以通過顯示地址表我們知道起始位置開始后1602一行只能顯示16個(gè)字符,但是一行的地址卻遠(yuǎn)遠(yuǎn)不止16個(gè)。大家可以看到第一行顯示地址是從00~27,然而能顯示在1602可見范圍的只有00~0F,后面的位置其實(shí)就是起到一個(gè)緩沖的作用,你完全可以讓字符數(shù)據(jù)存在在10地址后的RAM中,只不過,我們無法看到就是了,如果存在10前的地址我們就能看到顯示。
因此,我們可以先把顯示起始地址設(shè)在10地址后的某一個(gè)位置,然后讓字符顯示地址每次加1,當(dāng)加到0F時(shí),我們就可以看到字符串第一個(gè)字符出現(xiàn)在1602的最末一位,然后繼續(xù)向前移動(dòng)。
下面給出一段字符串移動(dòng)顯示的例子:
void display_lcd_byte(uchar y,uchar x,uchar z)
{
if(y)
{
x+=0x40;
}
x+=0x80;
write(x,0);
write(z,1);
}
void display_lcd_text(uchar y,uchar x,uchar table[])
{
uchar z=0;
uchar t;
t=strlen(table)+x;
while(x<t)
{
display_lcd_byte(y,x,table[z]);
x++;
z++;
}
display_lcd_byte(y,x,' ');
}
//前兩個(gè)子程序是顯示子程序
void main()
{
uchar i;
LCD1602();
init();
for(i=16;i>=0;i--) //這里的循環(huán)就是為了字符串從后往前顯示
{
display_lcd_text(0,i,table0); //i減一次,首個(gè)字符就往前去一位
delay(200);
}
while(1);
}
二、顯示自定義字符:
要想顯示自定義字符,首先就得取得想要的圖形或者字符的字模數(shù)組,可以通過手動(dòng)提取的方法,取得相應(yīng)的字模。
如下圖所示,對應(yīng)一個(gè)字符顯示區(qū)域。每8個(gè)字節(jié),組成一個(gè)點(diǎn)陣數(shù)組。
要想讓某一格子顯示就讓那一位為1,每行自定義5位,全白為0x00;全黑為0x1f。一共8行,每行一位數(shù)據(jù)。
將生成的點(diǎn)陣數(shù)組保存到CGRAM存儲(chǔ)器中,生成自定義字符。1602內(nèi)部CGRAM用于自定義的字符點(diǎn)陣的存儲(chǔ),總共64字節(jié)。由上一步點(diǎn)陣提取可知,每一個(gè)字符由8個(gè)字節(jié)數(shù)據(jù)組成。所以64字節(jié)CGRAM存儲(chǔ)器,能夠存儲(chǔ)8組自定義字符的點(diǎn)陣數(shù)組。按照CGRAM地址劃分為 0~7為第一組,8~15為第二組,依次類推56~63為第8組數(shù)據(jù)。
CHARACTER CODE是數(shù)據(jù)的顯示地址,0-7的范圍,能存儲(chǔ)8位自定義的字符。(能存八個(gè)自定義,每個(gè)字符存放的)
CGRAM ADDRESS是存儲(chǔ)數(shù)據(jù)的地址,從0-63共64個(gè)字節(jié).存儲(chǔ)64個(gè)數(shù)據(jù)。我們寫入的數(shù)據(jù)是0x40~0x7F,共128位。(把字符數(shù)組內(nèi)的8個(gè)數(shù)送進(jìn)這8個(gè)地址,每存完一個(gè)字符的8位,下次地址直接轉(zhuǎn)到0x48)
CGRAM DATA 字模每一行5位數(shù)據(jù)存
內(nèi)部常用字符顯示時(shí),顯示編碼是從0x20開始的。0x00~0x0F是專門留給自定義字符顯示的。0x00~0x07和0x08~0x0F內(nèi)容是一樣的。例如:調(diào)用0x01 位置和0x09位置,顯示的內(nèi)容是一樣的。
LCD1602自定義顯示字符的方式共四步,如下面所示:
1.設(shè)置向CGRAM中存入這個(gè)數(shù)據(jù).初始地址是0x40。然后存一位向后加8,
總共能存8位自定義的字符。
2.然后可以把自定義的數(shù)據(jù)送入到LCD的CGRAM中。
3.向LCD寫指令,送入需要顯示數(shù)據(jù)的地址。
4.向LCD寫指令,把顯示的數(shù)據(jù)指向LCD的CGRAM存儲(chǔ)的位置,顯示出自定義字符。
舉例如下:
1.建立一個(gè)字符數(shù)組;
uchar LCD_Data1[]={0x01,0x03,0x1D,0x11,0x1D,0x03,0x01,0x00};
2.設(shè)置CGRAM地址,寫指令;
Write_LCD(0x40,0);
3.把數(shù)據(jù)送入CGRAM地址內(nèi);
for(i = 0; i < 8 ; i ++ )
{
Write_LCD(LCD_Data1[i],1);
}
4.寫需要顯示的位置指令;
Write_LCD(0x80,0);
5.把CGRAM的0位的數(shù)據(jù)送向LCD1602,顯示數(shù)據(jù)存儲(chǔ)的數(shù)據(jù);
Write_LCD(0x00,1);