快速掌握—— LCD1602液晶顯示
一、 LCD1602 簡介
簡介部分資料來源於百度百科:
實物圖:
Proteus器件圖:
器件號: LM016L
簡介:
LCD1602
16——表示每行顯示16個字符
02——表示顯示兩行
LCD1602是一種字符點陣型顯示模塊,如下圖 ,每個字符是由 5X8 的點陣組成。
引腳介紹:
引腳1:VSS爲地電源。
引腳2:VDD接5V正電源。
引腳3:VL爲液晶顯示器對比度調整端,接正電源時對比度最弱,接地時對比度最高。引腳4:RS爲寄存器選擇腳,高電平時選擇數據寄存器、低電平時選擇指令寄存器。
引腳5:R/W爲讀/寫信號線,高電平時進行讀操作,低電平時進行寫操作。
當RS和R/W共同 爲低電平時可以寫入指令或顯示地址;當RS爲低電平,R/W爲高電平時,可以讀忙信號;當 RS爲高電平,R/W爲低電平時,可以寫入數據。引腳6:E端爲使能端,當E端由高電平跳變爲低電平時(下降沿有效),液晶模塊執行命令。
引腳7 ~ 14:D0~D7爲8位雙向數據線。
引腳15:背光源正極。
引腳16:背光源負極
指令集(11條指令):
表1
(1)指令1:清屏。指令碼01H,光標復位到地址00H。
(2)指令2:光標復位。光標復位到地址00H。
(3)指令3:輸入方式設置。
其中,I/D表示光標的移動方向,高電平右移,低電平左移;S表示顯示屏上所有文字是否左移或右移,高電平表示有效,低電平表示無效。(4)指令4:顯示開關控制。
其中,D用於控制整體顯示的開與關,高電平表示開顯示,低電平表示關顯示;C用於控制光標的開與關,高電平表示有光標,低電平表示無光標;B用於控制光標是否閃爍,高電平閃爍,低電平不閃爍。(5)指令5:光標或字符移位控制。
其中,S/C表示在高電平時移動顯示的文字,低電平時移動光標。(6)指令6:功能設置命令。
其中,DL表示在高電平時爲4位總線,低電平時爲8位總線;N表示在低電平時爲單行顯示,高電平時雙行顯示;F表示在低電平時顯示5×7的點陣字符,高電平時顯示5×10的點陣字符。(7)指令7:字符發生器RAM地址設置。
(8)指令8:DDRAM地址設置。
(9)指令9:讀忙信號和光標地址。
其中,BF爲忙標誌位,高電平表示忙,此時模塊不能接收命令或數據,如果爲低電平則表示不忙。(10)指令10:寫數據。
(11)指令11:讀數據。
常用指令:
0x80+0xdd:dd爲地址,這條命令用於設置顯示起點座標
0x0c :開顯示,無光標,光標不閃爍 。一般做帶鍵盤輸入的才加入光標,如計算器。常用的計量顯示不顯示光標。
0x06 :寫一個數據,地址指針加1,由1602地址表可以看出,實際上就是設置成從左往右寫數據而已。
0x38 : 設置顯示模式,16x2顯示 5x7點陣,8位數據接口。端口不夠用時,這個命令也可以換用4位數據接口的。
0x01 :清屏。
表2
二、顯示原理
如上圖,每個字符由 5X8 點陣組成(也可選用 5X10),想要實現顯示,只需如下圖:
例:以 5X7 點陣爲例, 顯示字符 A
0 代表滅,1 代表亮
只需將想要顯示的字符的對應位置1,就能顯示該字符
LCD1602 固化了字模寄存器,即 CGROM 和 CGRAM,存儲了192個常用字符的字模。
字模庫:
該表 行是低四位,列是高四位
想要顯示哪個字符,只需查表,換算爲十六進制,寫入LCD1602即可。
例: 想要顯示字符 A
需要向 LCD1602 寫入 0x41(0100 0001)
三、顯示位置
LCD1602 實際有80個字節的DDRAM,
只不過 LCD1602 只有 16X2 個位置,後面很多位置顯示不出來,可以使用 指令5“光標或顯示移動指令” 使字符慢慢移動到可見的顯示範圍內,看到字符的移動效果。
所以LCD1602的實際顯示位置是,第一行:00 ~ 0F,第二行:40 ~ 4F
注意:
如圖, 指令8,D7位恆爲 1,
在實際向LCD1602傳入數據顯示地址時,需要 < 地址+0x80 >
例:向LCD1602的第一行第一列寫數據,傳入的地址應爲: (0x00+0x80)
四、LCD1602 操作
①寫操作時序圖:
②讀操作時序圖:
③時序時間參數:
1、忙檢測
爲什麼需要忙檢測??
單片機和 LCD1602 的工作速度存在差異,單片機速度快,所以單片機向 LCD1602 傳數據時,LCD1602 可能正在處理上一次的數據,處在忙狀態,爲了防止數據丟失或出錯,所以單片機就需要等待,待 LCD1602 處理完時在進行下一次數據的傳送。
代碼:
由指令9,需判斷 BF 的狀態
/****************** 忙檢測 *********************
LCD_Data 與 0x80 進行或運算,判斷 bit7 位狀態
若 LCD_Data 的 bit7 位是 0,則 LCD 不忙
若 LCD_Data 的 bit7 位是 1,則 LCD 忙
***********************************************/
void LCD_Check_Busy(){
uchar temp;
LCD_Data = 0xff // 十六進制:1111 1111
LCD_RS = 0; // 0 指令
LCD_RW = 1; // 1 讀
do{
LCD_E = 1; // 拉高
temp = LCD_Data; // 將 LCD 狀態保存在 temp 中,用於判忙
LCD_E = 0; // 負跳變使能
}while(temp & 0x80); // 結果爲 1,LCD 忙,繼續循環;結果爲 0,LCD 不忙,可以進行後面的操作
}
2、初始化
爲什麼初始化?
在 LCD1602 進行顯示前,需要進行輸入方式設置,顯示開關設置,光標、畫面移位設置,功能設置等,確定 LCD1602 的工作方式。
代碼:
/****************** 初始化 *********************
指令1 ——清屏
指令3 ——AC加1,畫面不動
指令4 ——顯示開,光標關,閃爍關
指令6 ——8位數據接口,兩行顯示,5X7點陣
指令8 ——從第一行第一列開始顯示
***********************************************/
void LCD_Init(){
LCD_Write_Cmd(0x01); // 指令1 ——清屏
LCD_Write_Cmd(0x06); // 指令3 ——AC加1,畫面不動
LCD_Write_Cmd(0x0c); // 指令4 ——顯示開,光標關,閃爍關
LCD_Write_Cmd(0x38); // 指令6 ——8位數據接口,兩行顯示,5X7點陣
LCD_Write_Cmd(0x80); // 指令8 ——從第一行第一列開始顯示
}
3、寫命令
代碼:
/****************** 寫命令 *********************
RS=0 指令
RW=0 寫操作
E=0 開始爲 0
寫入指令(給單片機 P3 端口)
延時 2ms
E=1 拉高
延時5ms,寫入指令有效
E=0 負跳變,指令寫入LCD
***********************************************/
void LCD_Write_Cmd(uchar Cmd){
LCD_Check_Busy(); // 判忙
LCD_RS = 0; // 0 指令
LCD_RW = 0; // 0 寫
LCD_E = 0;
LCD_Data = Cmd; // 送指令
delay_1ms(2);
LCD_E = 1; // 拉高
delay_1ms(5);
LCD_E = 0;
}
4、寫數據
代碼:
/****************** 寫數據 *********************
RS=1 數據
RW=0 寫操作
E=0 開始爲 0
寫入數據(給單片機 P3 端口)
延時 2ms
E=1 拉高
延時 7ms,寫入數據有效
E=0 負跳變,數據寫入LCD
***********************************************/
void LCD_Write_Data(uchar Data){
LCD_Check_Busy(); // 判忙
LCD_RS = 1; // 1 數據
LCD_RW = 0; // 0 寫
LCD_E = 0;
LCD_Data = Data; // 送數據
delay_1ms(2);
LCD_E = 1; // 拉高
delay_1ms(7);
LCD_E = 0;
}
實驗一:顯示Hello World !
結果展示:
代碼:
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
#define LCD_Data P3 // 數據端口
sbit LCD_RS = P2^0; // 數據(1)/ 指令(0)
sbit LCD_RW = P2^1; // 讀(1) / 寫(0)
sbit LCD_E = P2^2; // 使能(負跳變起作用)
void delay_1ms(uint c){ // 延時函數,無誤差
uchar a,b;
for(;c>0;c--)
{
for(b=199;b>0;b--)
{
for(a=1;a>0;a--);
}
}
}
/****************** 忙檢測 *********************
LCD_Data 與 0x80 進行或運算,判斷 bit7 位狀態
若 LCD_Data 的 bit7 位是 0,則 LCD 不忙
若 LCD_Data 的 bit7 位是 1,則 LCD 忙
***********************************************/
void LCD_Check_Busy(){
uchar temp;
LCD_Data = 0xff; // 十六進制:1111 1111
LCD_RS = 0; // 0 指令
LCD_RW = 1; // 1 讀
do{
LCD_E = 1; // 拉高
temp = LCD_Data; // 將 LCD 狀態保存在 temp 中,用於判忙
LCD_E = 0; // 負跳變使能
}while(temp & 0x80); // 結果爲 1,LCD 忙,繼續循環;結果爲 0,LCD 不忙,可以進行後面的操作
}
/****************** 寫指令 *********************
RS=0 指令
RW=0 寫操作
E=0 開始爲 0
寫入指令(給單片機 P3 端口)
延時 2ms
E=1 拉高
延時5ms,寫入指令有效
E=0 負跳變,指令寫入LCD
***********************************************/
void LCD_Write_Cmd(uchar Cmd){
LCD_Check_Busy(); // 判忙
LCD_RS = 0; // 0 指令
LCD_RW = 0; // 0 寫
LCD_E = 0;
LCD_Data = Cmd; // 送指令
delay_1ms(2);
LCD_E = 1;
delay_1ms(5);
LCD_E = 0;
}
/****************** 寫數據 *********************
RS=1 數據
RW=0 寫操作
E=0 開始爲 0
寫入數據(給單片機 P3 端口)
延時 2ms
E=1 拉高
延時 7ms,寫入數據有效
E=0 負跳變,數據寫入LCD
***********************************************/
void LCD_Write_Data(uchar Data){
LCD_Check_Busy(); // 判忙
LCD_RS = 1; // 1 數據
LCD_RW = 0; // 0 寫
LCD_E = 0;
LCD_Data = Data; // 送數據
delay_1ms(2);
LCD_E = 1;
delay_1ms(7);
LCD_E = 0;
}
/****************** 初始化 *********************
指令1 ——清屏
指令3 ——AC加1,畫面不動
指令4 ——顯示開,光標關,閃爍關
指令6 ——8位數據接口,兩行顯示,5X7點陣
指令8 ——從第一行第一列開始顯示
***********************************************/
void LCD_Init(){
LCD_Write_Cmd(0x01); // 指令1 ——清屏
LCD_Write_Cmd(0x06); // 指令3 ——AC加1,畫面不動
LCD_Write_Cmd(0x0c); // 指令4 ——顯示開,光標關,閃爍關
LCD_Write_Cmd(0x38); // 指令6 ——8位數據接口,兩行顯示,5X7點陣
LCD_Write_Cmd(0x80); // 指令8 ——從第一行第一列開始顯示
}
/****************** 主函數 *********************
***********************************************/
void main()
{
uchar code Data[]="Hello World !"; // 要顯示的數據
uchar i;
LCD_Init(); // LCD1602 初始化
LCD_Write_Cmd(0x00+0x80); // 第一行第一列地址,0x00 可以省去,爲了講明白故加上
for(i=0; i<16; i++){
LCD_Write_Data(Data[i]);
}
while(1);
}
實驗二:兩行顯示
要求:
第一行顯示 0123456789012345
第二行顯示 abcdefghABCDEFGH
結果展示:
代碼:
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
#define LCD_Data P3 // 數據端口
sbit LCD_RS = P2^0; // 數據(1)/ 指令(0)
sbit LCD_RW = P2^1; // 讀(1) / 寫(0)
sbit LCD_E = P2^2; // 使能(負跳變起作用)
void delay_1ms(uint c){ // 延時函數,無誤差
uchar a,b;
for(;c>0;c--)
{
for(b=199;b>0;b--)
{
for(a=1;a>0;a--);
}
}
}
/****************** 忙檢測 *********************
LCD_Data 與 0x80 進行或運算,判斷 bit7 位狀態
若 LCD_Data 的 bit7 位是 0,則 LCD 不忙
若 LCD_Data 的 bit7 位是 1,則 LCD 忙
***********************************************/
void LCD_Check_Busy(){
uchar temp;
LCD_Data = 0xff; // 十六進制:1111 1111
LCD_RS = 0; // 0 指令
LCD_RW = 1; // 1 讀
do{
LCD_E = 1; // 拉高
temp = LCD_Data; // 將 LCD 狀態保存在 temp 中,用於判忙
LCD_E = 0; // 負跳變使能
}while(temp & 0x80); // 結果爲 1,LCD 忙,繼續循環;結果爲 0,LCD 不忙,可以進行後面的操作
}
/****************** 寫指令 *********************
RS=0 指令
RW=0 寫操作
E=0 開始爲 0
寫入指令(給單片機 P3 端口)
延時 2ms
E=1 拉高
延時5ms,寫入指令有效
E=0 負跳變,指令寫入LCD
***********************************************/
void LCD_Write_Cmd(uchar Cmd){
LCD_Check_Busy(); // 判忙
LCD_RS = 0; // 0 指令
LCD_RW = 0; // 0 寫
LCD_E = 0;
LCD_Data = Cmd; // 送指令
delay_1ms(2);
LCD_E = 1;
delay_1ms(5);
LCD_E = 0;
}
/****************** 寫數據 *********************
RS=1 數據
RW=0 寫操作
E=0 開始爲 0
寫入數據(給單片機 P3 端口)
延時 2ms
E=1 拉高
延時 7ms,寫入數據有效
E=0 負跳變,數據寫入LCD
***********************************************/
void LCD_Write_Data(uchar Data){
LCD_Check_Busy(); // 判忙
LCD_RS = 1; // 1 數據
LCD_RW = 0; // 0 寫
LCD_E = 0;
LCD_Data = Data; // 送數據
delay_1ms(2);
LCD_E = 1;
delay_1ms(7);
LCD_E = 0;
}
/****************** 初始化 *********************
指令1 ——清屏
指令3 ——AC加1,畫面不動
指令4 ——顯示開,光標關,閃爍關
指令6 ——8位數據接口,兩行顯示,5X7點陣
指令8 ——從第一行第一列開始顯示
***********************************************/
void LCD_Init(){
LCD_Write_Cmd(0x01); // 指令1 ——清屏
LCD_Write_Cmd(0x06); // 指令3 ——AC加1,畫面不動
LCD_Write_Cmd(0x0c); // 指令4 ——顯示開,光標關,閃爍關
LCD_Write_Cmd(0x38); // 指令6 ——8位數據接口,兩行顯示,5X7點陣
LCD_Write_Cmd(0x80); // 指令8 ——從第一行第一列開始顯示
}
/****************** 主函數 *********************
***********************************************/
void main()
{
uchar code Data_1[]="0123456789012345"; // 第一行要顯示的數據
uchar code Data_2[]="abcdefghABCDEFGH"; // 第二行要顯示的數據
uchar i;
LCD_Init(); // LCD1602 初始化
LCD_Write_Cmd(0x00+0x80); // 第一行第一列地址
for(i=0; i<16; i++){
LCD_Write_Data(Data_1[i]);
}
LCD_Write_Cmd(0x40+0x80); // 第二行第一列地址
for(i=0; i<16; i++){
LCD_Write_Data(Data_2[i]);
}
while(1);
}
實驗三:溫度單位顯示
確定字模:
如圖,查字模表得:
溫度單位由 1101 1111 和 0100 0011 組成
換算位十六進制爲:0xdf 和 0x43 組成
結果展示:
代碼:
直接替換爲以下主函數即可
/****************** 主函數 *********************
***********************************************/
void main()
{
LCD_Init(); // LCD1602 初始化
LCD_Write_Cmd(0x00+0x80); // 第一行第一列地址
LCD_Write_Data(0xdf);
LCD_Write_Data(0x43);
while(1);
}
實驗四:單行滾動顯示
**要求:**在第一行滾動顯示字符串 “Good Good Study,Day Day Up !”
需要用到 指令5——0x18 //畫面左移一個字符位,光標不動
結果展示:
代碼:
直接替換位爲以下主函數即可
/****************** 主函數 *********************
***********************************************/
void main()
{
uchar i;
uchar code Data[]="Good Good Study,Day Day Up !";
LCD_Init(); // LCD1602 初始化
LCD_Write_Cmd(0x00+0x80); // 第一行第一列地址
for(i=0; i<28; i++){
LCD_Write_Data(Data[i]);
}
while(1){
delay_1ms(500); // 控制移動速度
LCD_Write_Cmd(0x18); // 指令5 ——畫面左移一個字符位,光標不動
}
}
實驗五:一行不動,一行動
要求:
第一行顯示 " I Love You “,固定不動
第二行顯示"Good Good Study,Day Day Up !”, 實現向左移動效果
結果展示:
代碼:
直接替換位爲以下主函數即可
/****************** 主函數 *********************
不使用整屏左移命令
使用指針進行右移
***********************************************/
void main()
{
uchar i, j=0, n=0;
uchar code Data_1[]=" I Love You "; // 第一行顯示,共十六個字符
uchar code Data_2[]="Good Good Study,Day Day Up !"; // 第二行顯示,共28個字符
LCD_Init(); // LCD1602 初始化
LCD_Write_Cmd(0x00+0x80); // 第一行第一列地址
for(i=0; i<16; i++){
LCD_Write_Data(Data_1[i]); // 顯示第一行
}
while(1){
LCD_Check_Busy(); // 判忙,若忙,不繼續執行
LCD_Write_Cmd(0x40+0x80); // 第二行第一列地址
for(j=n; j<28+n; j++){
LCD_Write_Data(Data_2[j]); // 顯示第二行
}
n++;
if(n >= (28-15)){ // 當數據移動到最後時,n 重置 0,停頓 500 ms,重新開始移動顯示
n = 0;
delay_1ms(500);
}
delay_1ms(200); // 控制移動速度
}
}
實驗六:自定義字模顯示
要求:
自定義數模
在第一行第一列顯示漢字 ——年
在第一行第三列顯示漢字 ——月
在第一行第五列顯示漢字 ——日
確定字模:
以 日 字爲例
結果展示:
代碼:
直接替換位爲以下主函數即可
/****************** 主函數 *********************
***********************************************/
void main(){
uchar i;
uchar code Data_0[]={0x0f, 0x12, 0x0f, 0x0a, 0x1f, 0x02, 0x02, 0x02}; // 漢字 年 的字模
uchar code Data_1[]={0x0f, 0x09, 0x0f, 0x09, 0x0f, 0x09, 0x0b, 0x11}; // 漢字 月 的字模
uchar code Data_2[]={0x1f, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x1f, 0x00}; // 漢字 日 的字模
LCD_Init(); // LCD1602 初始化
LCD_Write_Cmd(0x40); // 0100 0000; 指令 0x40 向 CGRAM 地址0 寫入自定義數據
for(i=0; i<8; i++){
LCD_Write_Data(Data_0[i]); // 寫入自定義字符字模
}
LCD_Write_Cmd(0x48); // 0100 1000; 指令 0x48 向 CGRAM 地址1 寫入自定義數據
for(i=0; i<8; i++){
LCD_Write_Data(Data_1[i]); // 寫入自定義字符字模
}
LCD_Write_Cmd(0x50); // 0101 0000; 指令 0x50 向 CGRAM 地址2 寫入自定義數據
for(i=0; i<8; i++){
LCD_Write_Data(Data_2[i]); // 寫入自定義字符字模
}
LCD_Write_Cmd(0x00+0x80); // 在第一行第一列顯示 第一個字符
LCD_Write_Data(0);
LCD_Write_Cmd(0x02+0x80); // 在第一行第三列顯示 第二個字符
LCD_Write_Data(1);
LCD_Write_Cmd(0x04+0x80); // 在第一行第五列顯示 第一個字符
LCD_Write_Data(2);
while(1);
}