本文為大大維原創,最早於博客園發表,轉載請注明出處!!!
一、前言:
LCD ( Liquid Crystal Display 的簡稱)液晶顯示器,已經逐漸替代CRT成為主流的顯示設備之一,因此也成為了單片機發燒友繞不過的話題之一;而LCD1602更是很多單片機發燒友最早接觸的字符型液晶顯示器。筆者經過一段時間的深入學習,對其驅動有了些許心得,特地記錄於此,以備以后查閱。
LCD1602,也叫1602液晶,是一種專門用來顯示字母、數字、符號等的點陣型液晶模塊,工業字符型液晶的一種,能夠同時顯示16x02即32個字符。主控芯片是HD44780或者其它兼容芯片,而市面上字符液晶大多數是基於HD44780液晶芯片的,控制原理是完全相同的。因此盡管LCD1602可顯示的內容十分有限,但在此花點時間是完全值得的。
二、LCD的一些背景知識:
<1>LCD1602基本參數及引腳功能:
1.LCD1602的分類:
LCD1602分為帶背光和不帶背光兩種,帶背光的比不帶背光的厚,是否帶背光在應用中並無差別,兩者尺寸差別如下圖10-54所示:
在本文中,筆者選用的是帶背光的一種。
2.基本參數:
顯示容量:16×2個字符
芯片工作電壓:4.5—5.5V
工作電流:2.0mA(5.0V)
模塊最佳工作電壓:5.0V
字符尺寸:2.95×4.35(W×H)mm
2.引腳功能說明:
1602LCD采用標准的14腳(無背光)或16腳(帶背光)接口,各引腳接口說明如下:
編號 |
符號 |
引腳說明 |
編號 |
符號 |
引腳說明 |
1 |
VSS |
電源地 |
9 |
D2 |
數據 |
2 |
VDD |
電源正極 |
10 |
D3 |
數據 |
3 |
VL |
液晶顯示偏壓 |
11 |
D4 |
數據 |
4 |
RS |
數據/命令選擇 |
12 |
D5 |
數據 |
5 |
R/W |
讀/寫選擇 |
13 |
D6 |
數據 |
6 |
E |
使能信號 |
14 |
D7 |
數據 |
7 |
D0 |
數據 |
15 |
BLA |
背光源正極 |
8 |
D1 |
數據 |
16 |
BLK |
背光源負極 |
各引腳說明:
第1腳:VSS為地電源。
第2腳:VDD接5V正電源。
第3腳:VL為液晶顯示器對比度調整端,接正電源時對比度最弱,接地時對比度最高,對比度過高時會產生“鬼影”,使用時可以通過一個10K的電位器調整對比度。
第4腳:RS為寄存器選擇,高電平時選擇數據寄存器、低電平時選擇指令寄存器。
第5腳:R/W為讀寫信號線,高電平時進行讀操作,低電平時進行寫操作。當RS和R/W共同為低電平時可以寫入指令或者顯示地址,當RS為低電平R/W為高電平時可以讀忙信號,當RS為高電平R/W為低電平時可以寫入數據。
第6腳:E端為使能端,當E端由高電平跳變成低電平時,液晶模塊執行命令。
第7~14腳:D0~D7為8位雙向數據線。
第15腳:背光源正極。
第16腳:背光源負極
<2>LCD1602的指令說明及時序:
1.與HD44780相兼容的芯片時序表如下:
讀狀態 |
輸入 |
RS=L,R/W=H,E=H |
輸出 |
D0—D7=狀態字 |
寫指令 |
輸入 |
RS=L,R/W=L,D0—D7=指令碼,E=高脈沖 |
輸出 |
無 |
讀數據 |
輸入 |
RS=H,R/W=H,E=H |
輸出 |
D0—D7=數據 |
寫數據 |
輸入 |
RS=H,R/W=L,D0—D7=數據,E=高脈沖 |
輸出 |
無 |
2.LCD1602的指令說明:
1602液晶模塊內部的控制器共有11條控制指令,如表所示:
序號 |
指令 |
RS |
R/W |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
1 |
清顯示 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
2 |
光標返回 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
* |
3 |
置輸入模式 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
I/D |
S |
4 |
顯示開/關控制 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
D |
C |
B |
5 |
光標或字符移位 |
0 |
0 |
0 |
0 |
0 |
1 |
S/C |
R/L |
* |
* |
6 |
置功能 |
0 |
0 |
0 |
0 |
1 |
DL |
N |
F |
* |
* |
7 |
置字符發生存貯器地址 |
0 |
0 |
0 |
1 |
字符發生存貯器地址 |
|||||
8 |
置數據存貯器地址 |
0 |
0 |
1 |
顯示數據存貯器地址 |
||||||
9 |
讀忙標志或地址 |
0 |
1 |
BF |
計數器地址 |
||||||
10 |
寫數到CGRAM或DDRAM |
1 |
0 |
要寫的數據內容 |
|||||||
11 |
從CGRAM或DDRAM讀數 |
1 |
1 |
讀出的數據內容 |
筆者在百度文庫中發現一個特別好的介紹1602指令集的文章,給大家分享如下:
<3>LCD1602的RAM地址映射及標准字庫表:
1.LCD1602的RAM地址映射
液晶顯示模塊是一個慢顯示器件,所以在執行每條指令之前一定要確認模塊的忙標志為低電平,表示不忙,否則此指令失效。要顯示字符時要先輸入顯示字符地址,也就是告訴模塊在哪里顯示字符,下圖是1602的內部顯示地址:
在對液晶模塊的初始化中要先設置其顯示模式,在液晶模塊顯示字符時光標是自動右移的,無需人工干預。每次輸入指令前都要判斷液晶模塊是否處於忙的狀態。
2.LCD1602的標准字庫表
1602液晶模塊內部的字符發生存儲器(CGROM)已經存儲了160個不同的點陣字符圖形,如下圖所示,這些字符有:阿拉伯數字、英文字母的大小寫、常用的符號、和日文假名等。
在使用標准字庫中的字符圖形時,無需自己制作字模即可直接使用,若要顯示漢字等標准字庫中沒有的元素,則需要自己在DDRAM中制作字模。
<4>LCD1602四線驅動的約定流程:
1、 寫入指令:
1) EN = 0 (LCD使能禁止)
2) 短延時
3) RS = 0 ,RW = 0 (准備寫入指令)
4) DATA = 指令的高4位(ATA代表D7、D6、D5、D4,下同)
5) 短延時
6) EN = 1 (LCD使能允許,指令寫入)
7) 短延時
8) EN = 0 (LCD使能禁止)
9) DATA = 指令的低4位
10) 短延時
11) EN = 1 (LCD使能允許,指令寫入)
12) 短延時
13) EN = 0 (LCD使能禁止)
14) 短延時
2、 寫入數據:
1) EN = 0 (LCD使能禁止)
2) 短延時
3) RS = 1 ,RW = 0 (准備寫入數據)
4) DATA = 數據的高4位
5) 短延時
6) EN = 1 (LCD使能允許,數據寫入)
7) 短延時
8) EN = 0 (LCD使能禁止)
9) DATA = 數據的低4位
10) 短延時
11) EN = 1 (LCD使能允許,數據寫入)
12) 短延時
13) EN = 0 (LCD使能禁止)
14) 短延時
三、關於LCD控制庫LiquidCrystal:
LiquidCrystal是Arduino的官方庫之一,他可以控制基於日立公司HD44780(或兼容)芯片集成的字符型LCD。該庫可以通過四線或者八線模式控制LCD。
無疑,LiquidCrystal庫可以使LCD的控制編程變得十分簡單!!!
筆者在學習中,發現了一個比較完整的LiquidCrystal庫的中文資料,鏈接如下:
下面,就一些常用的LiquidCrystal庫函數做一個簡單介紹:
1.LiquidCrystal()構造函數
2.begin()指定顯示方式
3.clear()清屏,光標置於左上角
4.home()接下來的字符從左上角顯示
5.setCursor(col,row)設置光標位置
6.print()LCD顯示字符
四、Arduino下的LCD1602的8線驅動模式:
<1>優缺點:
八線模式的優點是每位數據均從單獨的數據線傳輸,因此數據傳輸速度快。但缺點也是顯然的,需要占掉大量的端口。
<2>接線方式:
VL為液晶顯示器對比度調整端,有條件的話,最好使用一個10K的電位器來調整對比度。限於條件,筆者通過反復調試,最后選擇了4k電阻,效果也還可以。
<3>試驗代碼:
1.不使用LiquidCrystal庫
1 /*Arduino在8位接法下不使用LiquidCrystal庫 2 * 輸出歡迎界面和Hello World! 3 * 作者:大大維 4 * 2016/10/23 5 */ 6 7 int RS = 12; //數據/命令選擇器引腳(RS) 8 int RW = 11; //讀/寫選擇器引腳(R/W) 9 int DB[] = {3, 4, 5, 6, 7, 8, 9, 10}; //使用數組來定義總線需要的管腳(D0-D7) 10 int Enable = 2; //使能(E) 11 12 void LcdCommandWrite(int value) { 13 // 定義所有引腳 14 int i = 0; 15 digitalWrite(RS, LOW); 16 digitalWrite(RW, LOW); 17 for (i=DB[0]; i <= RS; i++) //總線賦值 18 { 19 //這里的&是二進制的“與”運算。取指令碼的最低位。 20 digitalWrite(i,value & 0x01); 21 value >>= 1; 22 } 23 digitalWrite(Enable,LOW); 24 delayMicroseconds(1); // 延時1us 25 digitalWrite(Enable,HIGH); 26 delayMicroseconds(1); // 延時1us 27 digitalWrite(Enable,LOW); 28 delayMicroseconds(1); // 延時1us 29 } 30 31 void LcdDataWrite(int value) { 32 // 定義所有引腳 33 int i = 0; 34 digitalWrite(RS, HIGH); 35 digitalWrite(RW, LOW); 36 for (i=DB[0]; i <= DB[7]; i++) { 37 digitalWrite(i,value & 0x01); 38 value >>= 1; 39 } 40 digitalWrite(Enable,LOW); 41 delayMicroseconds(1); // 延時1us 42 digitalWrite(Enable,HIGH); 43 delayMicroseconds(1); // 延時1us 44 digitalWrite(Enable,LOW); 45 delayMicroseconds(1); // 延時1us 46 } 47 48 void setup (void) { 49 int i = 0; 50 for (i=Enable; i <= RS; i++) { 51 pinMode(i,OUTPUT); 52 } 53 delay(100); 54 // 短暫的停頓后初始化LCD 55 // 用於LCD控制需要 56 LcdCommandWrite(0x38); // 設置為8-bit接口,2行顯示,5x7文字大小 57 delay(20); 58 LcdCommandWrite(0x06); // 輸入方式設定,自動增量,畫面沒有移位 59 delay(20); 60 LcdCommandWrite(0x0C); // 顯示設置,開啟顯示屏,光標關,無閃爍 61 delay(20); 62 } 63 64 void loop (void) { 65 LcdCommandWrite(0x01); // 屏幕清空,光標位置歸零 66 delay(10); 67 LcdCommandWrite(0x80+5); // 定義光標位置為第一行第五個位置 68 delay(10); 69 // 寫入歡迎信息 70 LcdDataWrite('W'); 71 LcdDataWrite('e'); 72 LcdDataWrite('l'); 73 LcdDataWrite('c'); 74 LcdDataWrite('o'); 75 LcdDataWrite('m'); 76 LcdDataWrite('e'); 77 delay(10); 78 LcdCommandWrite(0xc0+2); // 定義光標位置為第二行第二個位置 79 delay(10); 80 LcdDataWrite('I'); 81 LcdDataWrite(' '); 82 LcdDataWrite('a'); 83 LcdDataWrite('m'); 84 LcdDataWrite(' '); 85 LcdDataWrite('D'); 86 LcdDataWrite('a'); 87 LcdDataWrite('D'); 88 LcdDataWrite('a'); 89 LcdDataWrite('W'); 90 LcdDataWrite('e'); 91 LcdDataWrite('i'); 92 delay(3000); 93 LcdCommandWrite(0x01); // 屏幕清空,光標位置歸零 94 delay(10); 95 LcdCommandWrite(0x80+2); //定義光標位置為第一行第二個位置 96 delay(10); 97 LcdDataWrite('H'); 98 LcdDataWrite('e'); 99 LcdDataWrite('l'); 100 LcdDataWrite('l'); 101 LcdDataWrite('o'); 102 LcdDataWrite(' '); 103 LcdDataWrite('W'); 104 LcdDataWrite('o'); 105 LcdDataWrite('r'); 106 LcdDataWrite('l'); 107 LcdDataWrite('d'); 108 LcdDataWrite('!'); 109 delay(5000); 110 }
看到上面的代碼,是否覺得略顯復雜?但要是使用了LiquidCrystal庫,一切將變得十分簡單!!!
2.使用LiquidCrystal庫
1 /*Arduino在8位接法下使用LiquidCrystal庫 2 * 輸出歡迎界面和Hello World! 3 * 作者:大大維 4 * 2016/10/23 5 */ 6 #include<LiquidCrystal.h> 7 LiquidCrystal lcd(12,11,3,4,5,6,7,8,9,10); //初始化LCD 8 void setup(){ 9 lcd.begin(16,2); //設置LCD為16列*2行 10 } 11 void loop(){ 12 lcd.clear(); //清屏操作 13 lcd.setCursor(5,0); //設置光標位置為第一行,第六列 14 lcd.print("Welcome"); //在LCD上輸出信息 15 delay(10); //延時程序 16 lcd.setCursor(2,1); 17 lcd.print("I am DaDaWei"); 18 delay(3000); 19 lcd.clear(); 20 lcd.setCursor(2,0); 21 lcd.print("Hello World!"); 22 delay(5000); 23 }
<4>試驗展示:
五、Arduino下的LCD1602的四線驅動模式:
<1>優缺點:
四線模式相對於八線模式的優點是占用的端口更少,減少了四條數據線,也就是說八位的數據需要通過四條線來發送,那么會導致四線模式比八線模式數據速度慢些。
<2>接線方式:
<3>試驗代碼:
1.不使用LiquidCrystal庫
1 /*Arduino在4位接法下不使用LiquidCrystal庫 2 * 輸出歡迎界面和Hello World! 3 * 作者:大大維 4 * 2016/10/23 5 */ 6 7 int RS = 12; //數據/命令選擇器引腳(RS) 8 int RW = 11; //讀/寫選擇器引腳(R/W) 9 int DB[] = {6, 7, 8, 9}; //使用數組來定義總線需要的管腳(D0-D7) 10 int Enable = 10; //使能(E) 11 12 void LcdCommandWrite(int value) { 13 // 定義所有引腳 14 int i = 0; 15 digitalWrite(Enable,LOW); 16 delayMicroseconds(1); // 延時1us 17 digitalWrite(RS, LOW); 18 digitalWrite(RW, LOW); 19 20 /* 6 7 8 9 21 * D7 D6 D5 D4 22 * DB[0] DB[1] DB[2] DB[3] 23 */ 24 int temp=value & 0xf0;//取指令高4位 25 for (i=DB[0]; i <= 9; i++) 26 { 27 if((temp & 0x80)!=0)//取指令最高位 28 digitalWrite(i,HIGH); 29 else 30 digitalWrite(i,LOW ); 31 temp <<= 1; 32 } 33 delayMicroseconds(1); // 延時1us 34 digitalWrite(Enable,HIGH); 35 delayMicroseconds(1); // 延時1us 36 digitalWrite(Enable,LOW); 37 38 temp=(value & 0x0f)<<4; 39 for (i=DB[0]; i <= 9; i++) 40 { 41 if((temp & 0x80)!=0)//取指令最高位 42 digitalWrite(i,HIGH); 43 else 44 digitalWrite(i,LOW ); 45 temp <<= 1; 46 } 47 delayMicroseconds(1); // 延時1us 48 digitalWrite(Enable,HIGH); 49 delayMicroseconds(1); // 延時1us 50 digitalWrite(Enable,LOW); 51 delayMicroseconds(1); // 延時1us 52 } 53 54 void LcdDataWrite(int value) { 55 // 定義所有引腳 56 int i = 0; 57 digitalWrite(Enable,LOW); 58 delayMicroseconds(1); // 延時1us 59 digitalWrite(RS, HIGH); 60 digitalWrite(RW, LOW); 61 int temp=value& 0xf0; 62 for (i=DB[0]; i <= 9; i++) 63 { 64 if((temp & 0x80)!=0)//取指令最高位 65 digitalWrite(i,HIGH); 66 else 67 digitalWrite(i,LOW ); 68 temp <<= 1; 69 } 70 digitalWrite(Enable,HIGH); 71 delayMicroseconds(1); // 延時1us 72 digitalWrite(Enable,LOW); 73 74 temp=(value & 0x0f)<<4; 75 for (i=DB[0]; i <= 9; i++) 76 { 77 if((temp & 0x80)!=0)//取指令最高位 78 digitalWrite(i,HIGH); 79 else 80 digitalWrite(i,LOW ); 81 temp <<= 1; 82 } 83 delayMicroseconds(1); // 延時1us 84 digitalWrite(Enable,HIGH); 85 delayMicroseconds(1); // 延時1us 86 digitalWrite(Enable,LOW); 87 delayMicroseconds(1); // 延時1us 88 } 89 90 void setup (void) { 91 int i = 0; 92 for (i=Enable; i <= RS; i++) { 93 pinMode(i,OUTPUT); 94 } 95 delay(100); 96 // 短暫的停頓后初始化LCD 97 // 用於LCD控制需要 98 LcdCommandWrite(0x38); // 設置為8-bit接口,2行顯示,5x7文字大小 99 delay(20); 100 LcdCommandWrite(0x06); // 輸入方式設定,自動增量,畫面沒有移位 101 delay(20); 102 LcdCommandWrite(0x0C); // 顯示設置,開啟顯示屏,光標關,無閃爍 103 delay(20); 104 } 105 106 void loop (void) { 107 LcdCommandWrite(0x01); // 屏幕清空,光標位置歸零 108 delay(10); 109 LcdCommandWrite(0x80+5); // 定義光標位置為第一行第五個位置 110 delay(10); 111 // 寫入歡迎信息 112 LcdDataWrite('W'); 113 LcdDataWrite('e'); 114 LcdDataWrite('l'); 115 LcdDataWrite('c'); 116 LcdDataWrite('o'); 117 LcdDataWrite('m'); 118 LcdDataWrite('e'); 119 delay(10); 120 LcdCommandWrite(0xc0+2); // 定義光標位置為第二行第二個位置 121 delay(10); 122 LcdDataWrite('I'); 123 LcdDataWrite(' '); 124 LcdDataWrite('a'); 125 LcdDataWrite('m'); 126 LcdDataWrite(' '); 127 LcdDataWrite('D'); 128 LcdDataWrite('a'); 129 LcdDataWrite('D'); 130 LcdDataWrite('a'); 131 LcdDataWrite('W'); 132 LcdDataWrite('e'); 133 LcdDataWrite('i'); 134 delay(3000); 135 LcdCommandWrite(0x01); // 屏幕清空,光標位置歸零 136 delay(10); 137 LcdCommandWrite(0x80+2); //定義光標位置為第一行第二個位置 138 delay(10); 139 LcdDataWrite('H'); 140 LcdDataWrite('e'); 141 LcdDataWrite('l'); 142 LcdDataWrite('l'); 143 LcdDataWrite('o'); 144 LcdDataWrite(' '); 145 LcdDataWrite('W'); 146 LcdDataWrite('o'); 147 LcdDataWrite('r'); 148 LcdDataWrite('l'); 149 LcdDataWrite('d'); 150 LcdDataWrite('!'); 151 LcdCommandWrite(0xc0); // 定義光標位置為第二行起始位置 152 delay(10); 153 LcdDataWrite('4'); 154 LcdDataWrite('-'); 155 LcdDataWrite('B'); 156 LcdDataWrite('i'); 157 LcdDataWrite('t'); 158 LcdDataWrite('-'); 159 LcdDataWrite('I'); 160 LcdDataWrite('n'); 161 LcdDataWrite('t'); 162 LcdDataWrite('e'); 163 LcdDataWrite('r'); 164 LcdDataWrite('f'); 165 LcdDataWrite('a'); 166 LcdDataWrite('c'); 167 LcdDataWrite('e'); 168 delay(10); 169 delay(5000); 170 }
2.使用LiquidCrystal庫
1 /*Arduino在4位接法下使用LiquidCrystal庫 2 * 輸出歡迎界面和Hello World! 3 * 作者:大大維 4 * 2016/10/23 5 */ 6 #include<LiquidCrystal.h> 7 LiquidCrystal lcd(13,12,11,10,9,8,7); //初始化LCD 8 void setup(){ 9 lcd.begin(16,2); //設置LCD為16列*2行 10 } 11 void loop(){ 12 lcd.clear(); //清屏操作 13 lcd.setCursor(5,0); //設置光標位置為第一行,第六列 14 lcd.print("Welcome"); //在LCD上輸出信息 15 delay(10); //延時程序 16 lcd.setCursor(2,1); 17 lcd.print("I am DaDaWei"); 18 delay(3000); 19 lcd.clear(); 20 lcd.setCursor(2,0); 21 lcd.print("Hello World!"); 22 lcd.setCursor(0,1); 23 lcd.print("4-Bit-Interface"); 24 delay(5000); 25 }
<4>試驗展示: