简析LCD1602液晶驱动及在Arduino上的实例实现
这几日在倒腾新到的Arduino,比起普通单片机来,感觉写程序太简单了。不过和外设打交道还是没那么容易,比如今天要说的看似简单的LCD1602液晶,却费了我一整天才基本搞懂,不过还是有一个小问题没有实现/解决。好在不怎么用那个功能,就没有再去深究。
其实之前做课设时接触过LCD1602,不过那时用的是51,而且是八位数据线接法,当时就挺苦恼1602的连线的,16根连线在板子上飞,那叫壮观。所以,今天我特地实现并搞懂了LCD1602的四位数据线接法,并完成了驱动程序,除了那个读数据的功能没有实现。
假定大家对LCD1602已经有了一定认识,我们就先来看下LCD1602的指令:
序号 | 指令 | 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(or DDRAM) | 1 | 0 | 要写的数据内容 | |||||||
11 | 从CGRAM或DDRAM读数据 | 1 | 1 | 读出的数据内容 |
关于对上表格的具体代码说明:1602液晶模块的读写操作、屏幕和光标的操作都是通过指令编程来实现的。(说明:1为高电平、0为低电平)
指令1:清显示,指令码01H,光标复位到地址00H位置。
指令2:光标复位,光标返回到地址00H。
指令3:光标和显示模式设置
I/D:光标移动方向,高电平右移,低电平左移
S:屏幕上所有文字是否左移或者右移。高电平表示有效,低电平则无效。
指令4:显示开关控制。
D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示
C:控制光标的开与关,高电平表示有光标,低电平表示无光标
B:控制光标是否闪烁,高电平闪烁,低电平不闪烁。
指令5:光标或显示移位
S/C:高电平时移动显示的文字,低电平时移动光标。
指令6:功能设置命令
DL:高电平时为4位总线,低电平时为8位总线
N:低电平时为单行显示,高电平时双行显示
F: 低电平时显示5×7的点阵字符,高电平时显示5×10的点阵字符。
指令7:字符发生器RAM地址设置。
指令8:DDRAM地址设置。
指令9:读忙信号和光标地址
BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
指令10:写数据。
指令11:读数据。
可能大家看了上表有些困惑,怎么用呢?其实就是几条不重叠的指令,如果你想要设置成你自己的风格,对应上表指令填充好指定的位后换算成2位16进制,0xFF的形势。然后在初始化函数中或者显示之前对1602输出该指令,即可达到效果。但是上面那么多设置,有好多指令,怎么分先后呢?这点我不是很清楚,也没有去尝试,一般的手册给出了1602初始化指令的顺序,如下:
- 延时15 ms
- 写指令38H(不检测忙信号)
- 延时5 ms
- 写指令38H(不检测忙信号)
- 延时5 ms
- 写指令38H(不检测忙信号)
- 以后每次写指令、读/写数据操作均需要检测忙信号
- 写指令38H:显示模式设置
- 写指令08H:显示关闭
- 写指令01H:显示清屏
- 写指令06H:显示光标移动设置
- 写指令0CH:显示开及光标设置
至此,初始化完成。
注意:以上1~6条照写,8~12条则根据自己情况写。比如,我要用四位数据总线连接LCD1602显示,则第8条指令相应改为28H。如果要显示并闪烁光标,12条相应改为0FH。并且,如果想要在使用中对LCD1602设置进行修改,可随时对其赋相应指令。
同时给出LCD1602时序图,供大家参考。
读状态 输入: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=高脉冲 输出:无
下面给出LCD1602的四位数据线接法的驱动函数。
1: /***************************************************************
2: * LCD1602 四位数据线连接驱动+实例 For Arduino
3: *
4: *主要实现了在Arduino下四位数据线连接LCD1602液晶显示,未实现数据读取。
5: (这方面的资料网上似乎很少,反正我是没找到。手册也是五花八门,
6: 未见其介绍如何在四位数据线连接下获取LCD1602的数据。)
7: 代码中除函数Init_LCD1602中延迟时间较标准外,其它未予深究,
8: 请不要以此拍砖。另外,写指令和写数据函数
9: 中的方法引自他处,虽有些晦涩,不过理解后感觉挺精妙的,就没有改。
10: 若要更换显示风格设置,请自行修改初始化函数中的内容。
11: *作者:Wave
12: *联系方式:sxwangbo@live.com
13: *日期:2013年8月9日
14: ***************************************************************/
15:
16: /*如果要更改此三处引脚,请在初始化函数Init_LCD1602()
17: *或setup()中手动指定所用引脚为输出模式。这里的排序可能有点坑爹。
18: *开始是为了对应Arduino的LCD1602库的脚排序,方便调试。后来就没有改。*/
19: int LCD1602_RS=12;
20: int LCD1602_RW=10;
21: int LCD1602_EN=11;
22: //要求四脚连续并从小到大排序,依次对应LCD1602的D7、D6、D5、D4脚。
23: int DB[] = {2,3,4,5};
24: char str1[]="Welcome to";
25: char str2[]="Matrix.Studio";
26: char str3[]="It's My Arduino.";
27: char str4[]="Do U think of it?";
28: char str5[]="Arduino is a single-board microcontroller to "+
29: "make using electronics in multidisciplinary projects "+
30: "more accessible. The hardware consists of a simple open "+
31: "source hardware board designed around an 8-bit Atmel AVR "+
32: "microcontroller, or a 32-bit Atmel ARM. The software "+
33: "consists of a standard programming language compiler "+
34: "and a boot loader that executes on the microcontroller. ";
35:
36:
37: //判断LCD1602是否忙,遇忙等待
38: void Check_LCD1602_Busy()
39: {
40: digitalWrite(LCD1602_RS,LOW);
41: digitalWrite(LCD1602_RW,HIGH);
42: digitalWrite(LCD1602_EN,HIGH);
43: pinMode(DB[0],INPUT);
44: while(digitalRead(DB[0])==HIGH);
45: pinMode(DB[0],OUTPUT);
46: digitalWrite(LCD1602_EN,LOW);
47: return;
48: }
49:
50: //给LCD1602写指令
51: void Write_LCD1602_Command(int data)
52: {
53: Check_LCD1602_Busy();
54: Write_LCD1602_Init_Command(data);
55: }
56:
57: //给LCD1602写指令,未检测忙状态,仅供初始化用
58: void Write_LCD1602_Init_Command(int command)
59: {
60: int i,temp;
61: digitalWrite( LCD1602_RS,LOW);
62: digitalWrite( LCD1602_RW,LOW);
63: digitalWrite( LCD1602_EN,LOW);
64:
65: temp=command & 0xf0;
66: for (i=DB[0]; i < DB[0]+4; i++)
67: {
68: digitalWrite(i,temp & 0x80);
69: temp <<= 1;
70: }
71:
72: digitalWrite( LCD1602_EN,HIGH);
73: delayMicroseconds(1);
74: digitalWrite( LCD1602_EN,LOW);
75:
76: temp=(command & 0x0f)<<4;
77: for (i=DB[0]; i < DB[0]+5; i++)
78: {
79: digitalWrite(i,temp & 0x80);
80: temp <<= 1;
81: }
82:
83: digitalWrite( LCD1602_EN,HIGH);
84: delayMicroseconds(1);
85: digitalWrite( LCD1602_EN,LOW);
86: }
87:
88: //写数据到LCD1602上。
89: void Write_LCD1602_Data(int data)
90: {
91: Check_LCD1602_Busy();
92: int i=0,temp;
93: digitalWrite( LCD1602_RS,HIGH);
94: digitalWrite( LCD1602_RW,LOW);
95: digitalWrite( LCD1602_EN,LOW);
96:
97: temp=data & 0xf0;
98: for (i=DB[0]; i < DB[0]+4; i++)
99: {
100: digitalWrite(i,temp & 0x80);
101: temp <<= 1;
102: }
103:
104: digitalWrite( LCD1602_EN,HIGH);
105: delayMicroseconds(1);
106: digitalWrite( LCD1602_EN,LOW);
107:
108: temp=(data & 0x0f)<<4;
109: for (i=DB[0]; i < DB[0]+5; i++)
110: {
111: digitalWrite(i,temp & 0x80);
112: temp <<= 1;
113: }
114:
115: digitalWrite( LCD1602_EN,HIGH);
116: delayMicroseconds(1);
117: digitalWrite( LCD1602_EN,LOW);
118: }
119:
120: //清除指定屏幕区域内容,即以空格填充
121: void Clear_LCD1602_Display(int x, int y, int length)
122: {
123: int i=0;
124: Write_LCD1602_Command(0x80 + 0x40*x + y);
125: for(i=y;i<length;i++)
126: Write_LCD1602_Data(0x20);//0x20为空格对应ASCII码
127: }
128:
129: //将指定字符输出到LCD1602上。
130: //其中x为行(0~1),y为列(0~15),data为指定字符
131: void Write_LCD1602_Char(int x, int y, int data)
132: {
133: Write_LCD1602_Command(0x80 + 0x40*x + y);
134: Write_LCD1602_Data(data);
135: }
136:
137: //将指定字符串输出到LCD1602上。
138: //其中x为行(0~1),y为列(0~15),*s为指定字符串指针
139: void Write_LCD1602_String(int x,int y,char *s)
140: {
141: Write_LCD1602_Command(0x80 + 0x40*x + y);
142: while (*s) //写字符串
143: {
144: Write_LCD1602_Data(*s);
145: s ++;
146: }
147: }
148:
149: //将指定文段输出显示到LCD1602上。
150: //其中x为行(0~1),y为列(0~15),*s为指定字符串指针
151: void Write_LCD1602_Paragraph(int x, int y, char *s)
152: {
153: Write_LCD1602_Command(0x0F);
154: Write_LCD1602_Command(0x80 + 0x40*x + y);
155: while (*s) //写字符串
156: {
157: Write_LCD1602_Data(*s);
158: s ++;
159: if(*s==' ')
160: delay(500);
161: if(y++==15)
162: {
163: Write_LCD1602_Command(0xC0);
164: }
165: if(y==32)
166: {
167: delay(1000);
168: Write_LCD1602_Command(0x01);
169: Write_LCD1602_Command(0x80);
170: y=0;
171: }
172: delay(100);
173: }
174: Write_LCD1602_Command(0x0E);
175: delay(3000);
176: }
177:
178: //较标准的LCD1602初始化函数
179: void Init_LCD1602()
180: {
181: int i = 0;
182: for (i=DB[0]; i < DB[0]+4; i++)
183: {
184: pinMode(i,OUTPUT);
185: }
186: for (i=10; i < 13; i++)
187: {
188: pinMode(i,OUTPUT);
189: }
190:
191: delay(15);
192: Write_LCD1602_Init_Command(0x38);
193: delay(5);
194: Write_LCD1602_Init_Command(0x38);
195: delay(5);
196: Write_LCD1602_Init_Command(0x38);
197:
198: Write_LCD1602_Command(0x28); //4位数据线
199: Write_LCD1602_Command(0x08); //关闭显示
200: Write_LCD1602_Command(0x01); //清除屏幕内容
201: Write_LCD1602_Command(0x06); //光标右移,文字不移
202: Write_LCD1602_Command(0x0F); //打开显示,显示光标且闪烁
203: }
204:
205: void setup (void)
206: {
207: Init_LCD1602();
208: delay(50);
209: }
210:
211: void loop (void)
212: {
213: //清屏
214: Write_LCD1602_Command(0x01);
215: Write_LCD1602_String(0,0,str1);//第1行,第4个地址起
216: delay(50);
217: Write_LCD1602_String(1,0,str2);//第2行,第2个地址起
218: delay(2000);
219: Write_LCD1602_Command(0x01);
220: delay(50);
221: Write_LCD1602_String(0,0,str3);
222: delay(50);
223: Write_LCD1602_String(1,0,str4);
224: delay(2000);
225: Write_LCD1602_Command(0x01);
226: delay(50);
227: Write_LCD1602_Paragraph(0,0,str5);
228: }