标题：1602字符液晶
简介
工业字符型液晶，能够同时显示16x02即32个字符。（16列2行）
注：为了表示的方便 ，后文皆以1表示高电平，0表示低电平。
管脚功能
  引脚说明1602字符型LCD通常有14条引脚线或16条引脚线的LCD，多出来的2条线是背光电源线
VCC(15脚)和地线GND(16脚)，其控制原理与14脚的LCD完全一样，其中：
引脚
符号
功能说明
1
VSS
一般接地
2
VDD
接电源（+5V）
3
V0
液晶显示器对比度调整端，接正电源时对比度最弱，接地电源时对比度最高（对比度过高时会产生“鬼影”，使用时可以通过一个10K的电位器调整对比度）。
4
RS
RS为寄存器选择，高电平1时选择数据寄存器、低电平0时选择指令寄存器。
5
R/W
R/W为读写信号线，高电平(1)时进行读操作，低电平(0)时进行写操作。
6
E
E(或EN)端为使能(enable)端，
写操作时，下降沿使能。
读操作时，E高电平有效
7
DB0
低4位三态、 双向数据总线 0位（最低位）
8
DB1
低4位三态、 双向数据总线 1位
9
DB2
低4位三态、 双向数据总线 2位
10
DB3
低4位三态、 双向数据总线 3位
11
DB4
高4位三态、 双向数据总线 4位
12
DB5
高4位三态、 双向数据总线 5位
13
DB6
高4位三态、 双向数据总线 6位
14
DB7
高4位三态、 双向数据总线 7位（最高位）（也是busy flag）
15
BLA
背光电源正极
16
BLK
背光 电源负极
寄存器选择控制表
RS
R/W
操作说明
0
0
写入指令寄存器（清除屏等）
0
1
读busy flag（DB7），以及读取位址计数器（DB0~DB6）值
1
0
写入数据寄存器（显示各字型等）
1
1
从数据寄存器读取数据
注：关于E=H脉冲——开始时初始化E为0，然后置E为1，再清0.
busy flag（DB7）：在此位为1时，LCD忙，将无法再处理其他的指令要求。
字符集
1602液晶模块内部的字符发生存储器（CGROM)已经存储了160个不同的点阵字符图形，这些字符有：阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等，每一个字符都有一个固定的代码，比如大写的英文字母“A”的代码是01000001B（41H），显示时模块把地址41H中的点阵字符图形显示出来，我们就能看到字母“A”。
因为1602识别的是ASCII码，试验可以用ASCII码直接赋值，在单片机编程中还可以用字符型常量或变量赋值，如'A’。
以下是1602的16进制ASCII码表：
  （图片打开是大图）
读的时候，先读上面那列，再读左边那行，如：感叹号！的ASCII为0x21，字母B的ASCII为0x42（前面加0x表示十六进制）。
显示地址
1602字符液晶显示可分为上下两部分各16位进行显示，处于不同行时的字符显示地址如下
显示字符
1
2
3
4
5
6
7
8
9
10
11
12
...
第一行地址
00H
01H
02H
03H
04H
05H
06H
07H
08H
09H
0AH
0BH
...
第二行地址
40H
41H
42H
43H
44H
45H
46H
47H
48H
49H
4AH
4BH
...

指令集
1602通过D0~D7的8位数据端传输数据和指令。
显示模式设置： (初始化)
0011 1000 [0x38] 设置16×2显示，5×7点阵，8位数据接口；
显示开关及光标设置：(初始化)
0000 1DCB D显示(1有效)、C光标显示(1有效)、B光标闪烁(1有效)
0000 01NS N=1(读或写一个字符后地址指针加1 &光标加1)，
N=0(读或写一个字符后地址指针减1 &光标减1)，
S=1 且 N=1 (当写一个字符后，整屏显示左移)
s=0 当写一个字符后，整屏显示不移动
数据指针设置：
数据首地址为80H，所以数据地址为80H+地址码(0-27H，40-67H)
其他设置：
01H(显示清屏，数据指针=0，所有显示=0)；02H(显示回车，数据指针=0)。
通常推荐的初始化过程：
延时15ms
写指令38H
延时5ms
写指令38H
延时5ms
写指令38H
延时5ms
（以上都不检测忙信号）
（以下都要检测忙信号）
写指令38H
写指令08H 关闭显示
写指令01H 显示清屏
写指令06H 光标移动设置
写指令0cH 显示开及光标设置
完毕
Proteus仿真
使用Proteus仿真1602--即LM016L--依照数据手册说明可能遇到困难，可以尝试采用以下方案解决：
1、数据手册中可能介绍1602内部D0~D7已有上拉，可以使用P0口直接驱动。在Proteus里LM016L内部可能没有,应该人为
加上拉电阻。建议不要使用排阻，使用普通电阻一个一个拉应该可以解决问题；
2、可能碰到不能检测忙信号的问题，尝试使用延时把忙信号拖过去。
基本的读写时序图
读写操作时序如图1和2所示：
写操作时序：
  读操作时序：
  
程序代码（参考）
/#include<intrins.h> //包含NOP空指令的头文件
#define uchar unsigned char
#define uint unsigned int 
#define LCD1602_H 1 //宏定义手册中出现的H的定义
#define LCD1602_L 0 //宏定义手册中出现的L的定义
#define LCD1602_DAT 1 //数据
#define LCD1602_COM 0 //命令
#define LCD_15MS 300 //宏定义15MS延时需要的数值
#defineLCD_5MS 100 //宏定义 5MS延时需要的数值
/*显示模式指令*/
#define LCD_Display_mode 0X38 //设置16x2显示 5x7点阵 8位数据接口
/*显示开/关及光标设置*/
#define LCD_shows0 0X0C //开显示 不显示光标 光标不闪烁
#define LCD_shows2 0X0E //开显示 显示光标 光标不闪烁
#define LCD_shows1 0X0F //开显示 显示光标 光标 闪烁
#define LCD_shows3 0X08 //关显示 不显示光标 光标不闪烁
/*指针设置*/
#define LCD_cursor1 0X04 //写一个字符 地址指针减1
#define LCD_cursor2 0X05 //写一个字符 地址指针减1 并屏幕右移
#define LCD_cursor3 0X06 //写一个字符 地址指针加1 
#define LCD_cursor4 0X07 //写一个字符 地址指针加1 并屏幕左移
/*清屏指令*/
#define LCD_clear 0x01 //清屏指令 数据指针清零 所有显示清零
/*忙状态字*/
#define LCD_WAY 0x80 //状态字
/*宏定义显示起始地址*/
#define LCD_ADDH 0X80 //第一行地址0x80-0xA7
#define LCD_ADDL 0XC0 //第二行地址0xC0-0xE7
/*IO口定义*/
#define LCD1602_DATA P0 //宏定义8位数据线IO为P0口 D0~D7=P00~P07 8位数据线 D0=P00;
sbit LCD1602_RS=P2^5; //数据/命令选择端(H/L)
sbit LCD1602_RW=P2^6; //读/写选择端（H/L）
sbit LCD1602_E =P2^7; //使能信号
/*函数声明*/
void LCD1602_init(); //液晶初始化函数
void LCD1602_writecd(bit lcd_rs, uchar LCD1602_d);//写命令/数据 函数bit lcd_rs是数据还是命令 uchar LCD1602_d要写入的数据 
uchar LCD1602_readway(); //读忙状态函数 由写入和读取函数调用
uchar LCD1602_readata(); //读数据函数
void LCD_DELAY(uchar LCD_delay); //
/*液晶初始化函数*/
void LCD1602_init() //液晶初始化函数
{ LCD_DELAY(LCD_15MS); //延时15MS 初始化
LCD1602_writecd(LCD1602_COM,LCD_Display_mode);//写指令38H 设置16x2显示 5x7点阵 8位数据接口
LCD1602_writecd(LCD1602_COM,LCD_shows0); //开显示 不显示光标 光标不闪烁
LCD1602_writecd(LCD1602_COM,LCD_cursor1); //检查忙状态
LCD1602_writecd(LCD1602_COM,LCD_clear); //写指令01H：显示清屏 
}
/*液晶写命令/数据函数*/
void LCD1602_writecd(bit lcd_rs, uchar LCD1602_cd)//写命令/数据 函数
{ uchar LCD1602_NUM; //定义变量用来液晶无忙回答的退出死循环
LCD1602_NUM=255; //忙状态检测次数
while(LCD1602_readway()) //检查忙状态
{LCD1602_NUM--; //检测次数自减
if(LCD1602_NUM==0) //判断检测次数等于0
{break;} //退出循环判断忙
}
LCD1602_RW = LCD1602_L; //读/写选择端(H/L) 
LCD1602_RS = lcd_rs; //数据/命令选择端(H/L) 
LCD1602_DATA= LCD1602_cd; //IO口赋值 
LCD1602_E = LCD1602_H; //拉高使能信号开始传输数据
LCD1602_E = LCD1602_L; //拉低使能信号锁存数据
LCD1602_DATA= 0xff; //IO口数据清除 
}
/*忙状态读取函数*/
uchar LCD1602_readway()//读状态函数由写入和读取函数调用
{ uchar LCD1602_way; //状态字变量
LCD1602_DATA=0xff; //IO口数据清除 
LCD1602_RS = LCD1602_COM; //数据/命令选择端(H/L) 命令
LCD1602_RW = LCD1602_H; //读/写选择端(H/L)
LCD1602_E = LCD1602_H; //拉高使能信号开始接收状态
LCD1602_way =LCD1602_DATA; //读取状态
LCD1602_E = LCD1602_L; //拉低使能信号锁存数据
LCD1602_way =LCD1602_way&LCD_WAY;//取忙状态字
return(LCD1602_way); //返回状态字 
}
/*液晶读数据函数*/
uchar LCD1602_readata()//读数据函数
{ uchar LCD1602_data; //数据暂存变量
while(LCD1602_readway()); //检查忙状态 --------------------
LCD1602_DATA=0xff; //IO口数据清除 
LCD1602_RS = LCD1602_DAT; //数据/命令选择端(H/L) 数据
LCD1602_RW = LCD1602_H; //读/写选择端(H/L)
LCD1602_E = LCD1602_H; //拉高使能信号开始接收状态
LCD1602_data= LCD1602_DATA; //读取状态
LCD1602_E = LCD1602_L; //拉低使能信号锁存数据
return(LCD1602_data); //返回数据 
}
/*延时函数*/
void LCD_DELAY(uchar LCD_delay)//
{ uchar lcd_del;
while(LCD_delay--) //自减
{lcd_del=100;
while(lcd_del--);}
}
[1]