本文为大大维原创,最早于博客园发表,转载请注明出处!!!
一、前言:
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>试验展示: