目录
- 硬知识
-
- 显示特性
- 接口定义
- 操作时序
-
- 写操作时序
- 读操作时序
- 寄存器
- 忙标志位BF
- 地址计数器(AC)
- 显示数据寄存器(DDRAM)
- CGROM
- CGRAM
- 指令
-
- 清屏指令
- 光标归位指令
- 设置进入模式的指令
- 显示开关控制指令
- 设置显示屏或光标移动指令
- 功能设置指令
- 设定CGRAM地址指令
- 设定DDRAM地址指令
- 读取忙信号或AC地址指令
- 数据写入DDRAM或CGRAM指令
- 从 CGRAM或DDRAM阅读数据指令
- 示例程序
-
- LCD1602.c
- LCD1602.h
- 测试程序
-
- main.c
- 实验现象
- 仿真现象
-
- 八线制
- 四线制
普中51-单核-A2 STC89C52 Windows 10 20H2 Proteus 8 Frofessional v8.9 SP2 Keil uVision V5.29.0.0 PK51 Prof.Developers Kit Version:9.60.0.0
硬知识
摘自《通用1602 液晶显示模块使用手册LCD1602(中文资料)—— ball 2010-5-13整理》
显示特性
单5V电源电压长寿命、高可靠性 内置192个字符(160个5个字符)x7点阵字符和325x10点阵字符) 具有64个字节的自定义字符RAM,可自定义8个5x8点阵字符或4个5x11点阵字符 显示方式:STN、半透、正显 驱动:1/16DUTY,1/5BIAS 视角方向:6点 背光方式:底部LED 通信方式:4位或8位并口可选 适应标准接口特性MCS1和M6800系列MPU操作时序。
接口定义
操作时序
写操作时序
读操作时序
寄存器
该模块有两个8位寄存器:指令寄存器(IR)地址寄存器(DR),用户可以通过RS和R/W输入信号的组合选择指定的寄存器进行相应的操作。下表列出了组合选择。
,内部存储DDRAM和CGRAM数据中显示的指令代码和地址信息只能由MPU执行写作操作: ,暂时存储在内部MPU与模块内部DDRAM和CGRAM传输数据和内部操作之间的内部操作DR与DDRAM或者CGRAM自动进行数据传输:
忙标志位BF
忙标志BF-1.表示模块正在进行内部操作,此时不接受任何外部指令和数据。Rs-0,RW-当E为高电平时,BF输出到DB7.最好在每次操作前检测状态字,只有在确认之前BF-0之后,MPU访问模块
地址计数器(AC)
AC地址计数器是DDRAM或者CGRAM地址指针IR写入中指令码,自动发送指令码中携带的地址信息AC并行做出AC作为DDRAM地址指针还是CGRAM选择地址指针。 AC具有自动加1或减1的功能。DR与DDRAM或者CGRAM数据传输完成后,AC在RS=0,R/W=当E为高电平时,AC的内容送到DB6 ~ DB0;
显示数据寄存器(DDRAM)
DDRAM存储显示字符的字符代码的容量决定了模块中最多可显示的字符数量.DDRAM地址与LCD显示屏上显示位置的对应关系如下:
执行显示移位操作时,对应DDRAM以每行16个字符的显示为例,移位前后的地址对应关系如下:
CGROM
HD44780内置了192个常用字符的字模,存储在字符生成器中CGROM(Character Generator ROM)中。在CGROM模块以8位二进制数的形式生成了5x8点阵的字符字模组字符字模(一个字符对应一组字模),符字模是与显示字符点阵相对应的8x8矩阵位图数据(与点阵行相对应的矩阵行的高三位为“0"),同时每一组字符字模都有一个由其在CGROM中存放地址的高八位数据组成的字符码对应。
CGRAM
另外还有8个允许用户自定义的字符产生RAM,称为CGRAM(Character Generator RAM) 就单屏结构的模块而言,字符码地址范围为00H ~ FFH,其中00H ~ 07H字符码与用户在CGRAM中生成的自定义图形字符的字模组相对应:至于双屏或者多屏结构的模块,由于各显示屏结构部分的工作分别由独立的使能信号E控制,因而各结构部分间字符的发生互不影响,每一显示屏结构部分的字符码地址范围为00H ~ FFH,其中00H ~ 07H字符码与用户在CGRAM中生成的自定义图形字符的字模组相对应。
指令
清屏指令
光标归位指令
进入模式设置指令
显示开关控制指令
设定显示屏或光标移动方向指令
功能设定指令
设定CGRAM地址指令
设定DDRAM地址指令
读取忙信号或AC地址指令
数据写入DDRAM或CGRAM指令
从 CGRAM或DDRAM读出数据的指令
示例程序
见【51单片机快速入门指南】1:基础知识和工程创建
LCD1602.c
#include <STC89C5xRC.H>
#include "stdint.h"
#include "LCD1602.h"
//******************以下内容移植时需修改******************
void delay_ms(int i);
#define LCD1602_Port P0
sbit RS_Pin = P2 ^ 6; //寄存器选择位,RS位
sbit WR_Pin = P2 ^ 5; //读写选择位,RW
sbit EN_Pin = P2 ^ 7; //使能信号位,E位
void LCD1602_Delay()
{
//主频快就加延时
}
void LCD1602_RS_H()
{
RS_Pin = 1;
}
void LCD1602_RS_L()
{
RS_Pin = 0;
}
void LCD1602_WR_H()
{
WR_Pin = 1;
}
void LCD1602_WR_L()
{
WR_Pin = 0;
}
void LCD1602_EN_H()
{
EN_Pin = 1;
}
void LCD1602_EN_L()
{
EN_Pin = 0;
}
void LCD1602_Write_Port(uint8_t Data)
{
#ifdef USE_4_Pin
LCD1602_Port &= 0x0F;
LCD1602_Port |= (Data << 4);
#else
LCD1602_Port = Data;
#endif
}
uint8_t LCD1602_Read_Port()
{
#ifdef USE_4_Pin //4线下好像没有意义
return 0;
#else
return LCD1602_Port;
#endif
}
//******************以上内容移植时需修改******************
unsigned char code LCD1602_DIY_Char[64]= //8个自定义字符 地址为0x00~0x07
{
0x02,0x04,0x0F,0x12,0x0F,0x0A,0x1F,0x02,//年
0x0F,0x09,0x0F,0x09,0x0F,0x09,0x09,0x11,//月
0x1F,0x11,0x11,0x1F,0x11,0x11,0x1F,0x00,//日
0x16,0x09,0x08,0x08,0x08,0x09,0x06,0x00,//℃
0x00,0x11,0x0A,0x04,0x0A,0x11,0x00,0x00,//×
0x00,0x04,0x00,0x1F,0x00,0x04,0x00,0x00,//÷
0x00,0x0E,0x11,0x11,0x11,0x0A,0x1B,0x00,//Ω
0x00,0x11,0x11,0x11,0x19,0x16,0x10,0x00,//μ
};
/***************************************************** 函数功能:判断液晶模块的忙碌状态 返回值:1: 忙碌; 0: 不忙 ***************************************************/
uint8_t LCD1602_Busy_Test(void)
{
#ifdef USE_4_Pin
delay_ms(2);
return 0;
#else
return ((LCD1602_Operation(0, 1, 0) & 0x80) && 1);
#endif
}
uint8_t LCD1602_Operation(uint8_t Cmd_Data_Flag, uint8_t Write_Read_Flag, uint8_t Data)
{
if(Cmd_Data_Flag)
LCD1602_RS_H();
else
LCD1602_RS_L();
if(Write_Read_Flag)
LCD1602_WR_H();
else
LCD1602_WR_L();
LCD1602_Delay(); //给硬件反应时间
if(Write_Read_Flag)
{
LCD1602_EN_H();
LCD1602_Delay(); //给硬件反应时间
Data = LCD1602_Read_Port();
}
else
{
LCD1602_EN_L();
LCD1602_Write_Port(Data);
LCD1602_Delay(); //给硬件反应时间
LCD1602_EN_H();
LCD1602_Delay(); //给硬件反应时间
}
LCD1602_EN_L();
return Data;
}
/***************************************************** 函数功能:将一个字节写入液晶模块 入口参数:Byte 字节, Cmd_Data_Flag 0为命令, 1为数据 ***************************************************/
void LCD1602_Write_Byte(uint8_t Byte, uint8_t Cmd_Data_Flag)
{
while (LCD1602_Busy_Test()); //如果忙就等待
#ifdef USE_4_Pin
LCD1602_Operation(Cmd_Data_Flag, 0, Byte >> 4);
while (LCD1602_Busy_Test());
LCD1602_Operation(Cmd_Data_Flag, 0, Byte & 0x0F);
#else
LCD1602_Operation(Cmd_Data_Flag, 0, Byte);
#endif
}
void LCD1602_DIY_Char_Init(void)
{
unsigned char i;
LCD1602_Write_Byte(LCD1602_Set_CGRAM_Addr, 0); //开始写入你要显示的自定义字符、汉字代码
for(i = 0; i < 64; i++)
{
LCD1602_Write_Byte(LCD1602_DIY_Char[i], 1); //开始写入你要显示的自定义字符、汉字代码
}
}
/***************************************************** 函数功能:指定字符显示的实际地址 入口参数:row 行:0或1 col:列 ***************************************************/
void LCD1602_Write_Address(uint8_t row, uint8_t col)
{
if(row)
LCD1602_Write_Byte(col | 0xc0, 0);
else
LCD1602_Write_Byte(col | 0x80, 0); //显示位置的确定方法规定为"80H+地址码x"
}
void LCD1602_Write_Str(char *Str)
{
while (*Str) //检测字符串结束标志
{
LCD1602_Write_Byte(*Str++, 1); //发送当前字符
}
}
/***************************************************** 函数功能:对LCD的显示模式进行初始化设置 ***************************************************/
void LCD1602_Init(void)
{
#ifdef USE_4_Pin
LCD1602_Write_Byte(LCD1602_Cursor_Return, 0);
LCD1602_Write_Byte(LCD1602_Config | LCD1602_4PIN | LCD1602_2Row | LCD1602_5X7, 0);
#else
LCD1602_Write_Byte(LCD1602_Config | LCD1602_8PIN | LCD1602_2Row | LCD1602_5X7, 0);
#endif
LCD1602_Write_Byte(LCD1602_Display_Switch | Display_Enable | Cursor_Enable | Cursor_Blink_Enable, 0);
LCD1602_Write_Byte(LCD1602_Cursor_Screen_Mode | Cursor_Right_Mode | Screen_Static_Mode, 0);
LCD1602_DIY_Char_Init();
LCD1602_Write_Byte(LCD1602_Clr, 0); //清屏幕指令,将以前的显示内容清除
}
LCD1602.h
#ifndef LCD1602_H_
#define LCD1602_H_
#define USE_8_Pin
//#define USE_4_Pin
#ifdef USE_8_Pin
#ifdef USE_4_Pin
#error
#endif
#endif
#define LCD1602_Clr 0x01
#define LCD1602_Cursor_Return 0x02
#define LCD1602_Cursor_Screen_Mode 0x04
#define Cursor_Left_Mode 0x00
#define Cursor_Right_Mode 0x02
#define Screen_Static_Mode 0x00
#define Screen_Right_Mode 0x01
#define LCD1602_Display_Switch 0x08
#define Display_Disable 0x00
#define Display_Enable 0x04
#define Cursor_Disable 0x00
#define Cursor_Enable 0x02
#define Cursor_Blink_Disable 0x00
#define Cursor_Blink_Enable 0x01
#define LCD1602_Cursor_Screen_Move 0x10
#define Cursor_Left 0x00
#define Cursor_Right 0x04
#define Screen_Left 0x08
#define Screen_Right 0x0c
#define LCD1602_Config 0x20
#define LCD1602_4PIN 0x00
#define LCD1602_8PIN 0x10
#define LCD1602_1Row 0x00
#define LCD1602_2Row 0x08
#define LCD1602_5X7 0x00
#define LCD1602_5X10 0x04
#define LCD1602_Set_CGRAM_Addr 0x40
#define LCD1602_Set_DDRAM_Addr 0x80
uint8_t LCD1602_Busy_Test(void);
uint8_t LCD1602_Operation(uint8_t Cmd_Data_Flag, uint8_t Write_Read_Flag, uint8_t Data);
void LCD1602_Write_Byte(uint8_t Byte, uint8_t Cmd_Data_Flag);
void LCD1602_Write_Address(uint8_t row, uint8_t col);
void LCD1602_Write_Str(char *Str);
void LCD1602_Init(void);
#endif
测试程序
四线和八线在LCD1602.h中控制
main.c
#include <STC89C5xRC.H>
#include "intrins.h"
#include "stdint.h"
#include "LCD1602.h"
void Delay1ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
void delay_ms(int i)
{
while(i--)
Delay1ms();
}
void main(void)
{
char i;
LCD1602_Init();
LCD1602_Write_Address(0, 0);
LCD1602_Write_Str("0123456789ABCDEF");
LCD1602_Write_Address(1, 0);
for(i = 0; i < 8; ++i)
LCD1602_Write_Byte(i, 1);
while(1)
{
}
}
实验现象
仿真现象
需要注意的是,Proteus仿真时存储模式需设为Small才能正确显示,很神秘。