简析LCD1602液晶驱动及在Arduino上的实例实现

时间:2021-11-02 05:31:30

这几日在倒腾新到的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初始化指令的顺序,如下:

  1.  延时15 ms
  2.  写指令38H(不检测忙信号)
  3.  延时5 ms
  4.  写指令38H(不检测忙信号)
  5.  延时5 ms
  6.  写指令38H(不检测忙信号)
  7. 以后每次写指令、读/写数据操作均需要检测忙信号
  8.  写指令38H:显示模式设置
  9.  写指令08H:显示关闭
  10.  写指令01H:显示清屏
  11.  写指令06H:显示光标移动设置
  12.  写指令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:  } 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }