!文章内容如有错误或排版问题,请提交反馈,非常感谢!
LCD1602简介
LCD1602 是一种非常经典且广泛使用的字符型液晶显示模块(Liquid Crystal Display)。它的名称揭示了其核心特性:
- LCD: 液晶显示。
- 1602:
- 16: 表示每行最多可以显示 16 个字符。
- 02: 表示显示屏有 2 行。
主要特点
- 显示类型: 字符型。它内部集成了字符发生器(CGROM),只能显示预定义的字符(ASCII 码字符、少量日文假名、自定义字符)。不能像点阵屏那样自由绘制图形。
- 控制器: 绝大多数 LCD1602 模块使用的是 Hitachi HD44780 或其兼容芯片(如 KS0066, SPLC780D 等)作为控制器。这个控制器负责与单片机通信、管理显示内容、光标位置等。
- 接口: 通常提供并行接口(8位或4位模式),部分模块可能还支持 I2C 接口(需要额外的转接板)。
- 背光: 绝大多数模块带有 LED 背光(通常为蓝色或绿色背光,白色字符),方便在光线不足的环境下使用。背光通常有单独的引脚(A/K 或 LED+/LED-)控制。
- 功耗: 功耗较低,非常适合电池供电或低功耗应用。
- 视角: 视角相对较窄(通常称为 6 点钟视角),从屏幕正上方看效果最佳。
- 对比度调节: 通过一个电位器(通常标为 VO)调节显示对比度。连接到 V0 引脚。
引脚定义 (标准 16 引脚)
引脚号 | 符号 | 功能说明 |
1 | VSS | 电源地 (GND)。连接到系统的地线。 |
2 | VDD | 电源正极 (+5V)。模块工作电压通常是 5V。 |
3 | VO / V0 | 液晶显示对比度调节端。通过一个 10KΩ 电位器连接到 VSS (地) 来调节对比度。 |
4 | RS | 寄存器选择 (Register Select)。高电平(1):选择数据寄存器(DR);低电平(0):选择指令寄存器(IR)。 |
5 | R/W | 读写选择 (Read/Write)。高电平(1):读操作;低电平(0):写操作。通常将此脚接地(低电平),只进行写操作。 |
6 | E | 使能信号 (Enable)。下降沿(由高变低)触发锁存数据。操作的关键引脚。 |
7 | DB0 | 数据线 0 (Data Bus bit 0)。在 4 位模式下不使用。 |
8 | DB1 | 数据线 1 (Data Bus bit 1)。在 4 位模式下不使用。 |
9 | DB2 | 数据线 2 (Data Bus bit 2)。在 4 位模式下不使用。 |
10 | DB3 | 数据线 3 (Data Bus bit 3)。在 4 位模式下不使用。 |
11 | DB4 | 数据线 4 (Data Bus bit 4)。在 4 位模式下使用(作为高 4 位)。 |
12 | DB5 | 数据线 5 (Data Bus bit 5)。在 4 位模式下使用(作为高 4 位)。 |
13 | DB6 | 数据线 6 (Data Bus bit 6)。在 4 位模式下使用(作为高 4 位)。 |
14 | DB7 | 数据线 7 (Data Bus bit 7)。在 4 位模式下使用(作为高 4 位)。 |
15 | A / LED+ | 背光电源正极 (Anode / LED+)。通常接 +5V(需串联限流电阻,如 220Ω)。 |
16 | K / LED- | 背光电源负极 (Cathode / LED-)。接地 (GND)。 |
重要说明:
- 4位模式 vs 8位模式: 为了节省单片机的 I/O 口资源,强烈推荐使用 4 位数据模式。这样只需要使用 DB4-DB7 这 4 根数据线,DB0-DB3 悬空即可。初始化过程稍复杂一点,但之后的操作效率相同。本教程将重点介绍 4 位模式。
- R/W 引脚: 在绝大多数应用场景下,我们只需要向 LCD 写入指令或数据,不需要读取其状态。因此,通常将 R/W 引脚直接接地 (GND),使其始终处于写模式。
- 背光: 如果不需要背光,15 和 16 脚可以不接。如果需要背光,务必在 15 脚(LED+)串联一个限流电阻(如 220Ω)后再接 VDD (+5V),16 脚(LED-)接 GND。
内部存储器
HD44780 控制器管理着两块重要的内存区域:
- DDRAM (Display Data RAM – 显示数据 RAM):
- 存储当前屏幕上实际显示的字符代码。
- LCD1602 的 DDRAM 有 80 字节容量,但屏幕只显示前 32 个字节(16×2)。
- 第一行对应地址0x00 – 0x0F (实际显示 0x00 – 0x0F)。
- 第二行对应地址0x40 – 0x4F (实际显示 0x40 – 0x4F)。
- 通过设置 DDRAM 地址,可以将光标移动到屏幕的任意位置。
- CGROM (Character Generator ROM – 字符发生器 ROM):
- 固化在芯片内部,存储了标准的字符点阵图案(如 ASCII 字符、日文假名)。我们发送的字符代码(如 ‘A’ 的 ASCII 码 0x41)就是用来索引 CGROM 中的点阵数据。
- CGRAM (Character Generator RAM – 字符发生器 RAM):
- 一小块 RAM(通常 64 字节),允许用户自定义最多 8 个 5×8 点阵的字符图案。通过向 CGRAM 特定地址写入自定义点阵数据,然后就可以像使用标准字符一样使用它们。
常用指令集 (HD44780 兼容)
控制器通过向指令寄存器(IR)发送指令代码来控制 LCD 的行为。以下是一些最常用的指令(高位在前,低位在后):
指令 (Hex) | 指令 (Bin) | RS | R/W | 功能说明 | 执行时间 |
0x01 | 0000 0001 | 0 | 0 | 清屏 (Clear Display):DDRAM 全部写空格 (0x20),地址计数器归零 (AC=0),光标回原位。 | 1.64ms |
0x02 | 0000 0010 | 0 | 0 | 光标归位 (Return Home):地址计数器归零 (AC=0),光标回原位。DDRAM 内容不变。 | 1.64ms |
0x04 | 0000 0100 | 0 | 0 | 进入模式设置 (Entry Mode Set):设定光标移动方向和画面是否移动。 | 40us |
0000 01ID | I/D: 1=地址递增 (右移), 0=地址递减 (左移)。S: 1=显示移位, 0=不移动。 | ||||
0x06 | 0000 0110 | 0 | 0 | 常用设置:I/D=1 (地址递增,光标右移),S=0 (显示不移动)。 | 40us |
0x08 | 0000 1000 | 0 | 0 | 显示开关控制 (Display ON/OFF Control):控制显示、光标、闪烁开关。 | 40us |
0000 1DCB | D: 1=显示开, 0=显示关。C: 1=光标显示, 0=光标不显示。B: 1=光标闪烁, 0=光标不闪烁。 | ||||
0x0C | 0000 1100 | 0 | 0 | 常用设置:D=1 (显示开),C=0 (光标关),B=0 (闪烁关)。 | 40us |
0x10 | 0001 0000 | 0 | 0 | 光标或显示移位 (Cursor/Display Shift):移动光标或整个显示画面。 | 40us |
0001 SR00 | S/C: 1=移动显示, 0=移动光标。R/L: 1=右移, 0=左移。 | ||||
0x14 | 0001 0100 | 0 | 0 | 光标右移 (Cursor Right):S/C=0 (移动光标), R/L=1 (右移)。 | 40us |
0x18 | 0001 1000 | 0 | 0 | 显示右移 (Display Right):S/C=1 (移动显示), R/L=1 (右移)。 | 40us |
0x20 | 0010 0000 | 0 | 0 | 功能设定 (Function Set):设定数据位数、显示行数、字体。 | 40us |
0010 DLNF00 | DL: 1=8位, 0=4位。N: 1=2行显示, 0=1行显示。F: 1=5×10点阵, 0=5×8点阵。 | ||||
0x28 | 0010 1000 | 0 | 0 | 4位模式常用设置:DL=0 (4位接口),N=1 (2行显示),F=0 (5×8点阵)。 | 40us |
0x80 + addr | 1addr addr addr | 0 | 0 | 设置 DDRAM 地址 (Set DDRAM Address):将地址计数器 AC 设置为指定的 DDRAM 地址。addr 范围 0x00-0x7F。 | 40us |
0x40 + addr | 01addr addr addr | 0 | 0 | 设置 CGRAM 地址 (Set CGRAM Address):将地址计数器 AC 设置为指定的 CGRAM 地址。addr 范围 0x00-0x3F (每个字符 8 字节)。 | 40us |
注意: 指令执行时间是基于特定的时钟频率(通常 270KHz),实际使用时,发送指令后需要插入适当的延时(delay_us(40) 或 delay_ms(2) 对于清屏和归位)以确保指令完成。
故障诊断指南
现象 | 可能原因 | 解决方案 |
无显示 | 背光未供电 | 检查A/K引脚电压 |
显示全黑 | VO电压过高 | 调低对比度电压 |
显示乱码 | 初始化失败 | 增加上电延时>20ms |
仅第一行显示 | 总线接触不良 | 检查DB0-DB7连接 |
字符残缺 | 时序过快 | 增加EN脉冲宽度 |
51单片机驱动 LCD1602
典型硬件连接(核心电路示意)
LCD1602 引脚 | 51 单片机引脚 | 说明 |
VSS (1) | GND | 接地 |
VDD (2) | VCC (+5V) | 接电源正极 |
VO (3) | 10K电位器中间脚 | 电位器一端接 VCC,另一端接 GND |
RS (4) | P2.0 | 寄存器选择 (可任意分配 I/O 口) |
R/W (5) | P2.1 | 读写控制 |
E (6) | P2.2 | 使能信号 (可任意分配 I/O 口) |
P0.0-P0.7 | D0-D7 | 8位数据总线 |
A / LED+ (15) | VCC (+5V) | 通过一个 220Ω 限流电阻 接 VCC (如果需要背光) |
K / LED- (16) | GND | 接地 (如果需要背光) |
注意事项:
- 如果使用P0口,必须接10K排阻上拉(VCC->排阻->P0)
- VO未接电位器会导致显示全黑/全白
Keil C51驱动代码示例
#include <reg52.h> // 硬件连接定义 #define LCD_DATA P0 sbit LCD_RS = P2^0; sbit LCD_RW = P2^1; sbit LCD_EN = P2^2; // 毫秒级延时函数(11.0592MHz晶振) void delay_ms(unsigned int ms) { unsigned int i, j; for(i=0; i<ms; i++) for(j=0; j<114; j++); } // 检测LCD忙状态 void lcd_busy() { LCD_DATA = 0xFF; // 置P0口高电平准备输入 LCD_RS = 0; // 命令模式 LCD_RW = 1; // 读状态 LCD_EN = 1; // 使能开始 while(LCD_DATA & 0x80); // 检测D7位(忙标志) LCD_EN = 0; // 使能结束 } // 写命令到LCD void lcd_cmd(unsigned char cmd) { lcd_busy(); // 等待空闲 LCD_RS = 0; // 写入命令 LCD_RW = 0; // 写操作 LCD_DATA = cmd; // 命令字输出 LCD_EN = 1; // 产生下降沿 delay_ms(1); // 脉冲宽度>450ns LCD_EN = 0; } // 写数据到LCD void lcd_dat(unsigned char dat) { lcd_busy(); // 等待空闲 LCD_RS = 1; // 写入数据 LCD_RW = 0; // 写操作 LCD_DATA = dat; // 数据输出 LCD_EN = 1; // 产生下降沿 delay_ms(1); // 脉冲宽度>450ns LCD_EN = 0; } // LCD初始化 void lcd_init() { delay_ms(20); // 等待LCD上电稳定 lcd_cmd(0x38); // 设置8位接口,2行显示,5x7点阵 lcd_cmd(0x0C); // 开显示,关光标,不闪烁 lcd_cmd(0x06); // 写入后光标右移 lcd_cmd(0x01); // 清屏 delay_ms(5); // 清屏延时 } // 显示字符串 void lcd_show(unsigned char pos, char *str) { unsigned char addr; // 计算DDRAM地址(0x80为首地址) addr = (pos<16) ? (0x80+pos) : (0xC0+(pos-16)); lcd_cmd(addr); // 设置显示位置 while(*str != '\0') { lcd_dat(*str); // 逐字符显示 str++; } } void main() { lcd_init(); lcd_show(0, "Hello World!"); // 第一行显示 lcd_show(16, "STC89C52RC"); // 第二行显示 while(1); // 程序停止 }
51单片机驱动I2C接口的LCD1602
I2C接口的LCD1602通过转接板将并行接口转换为I2C协议,显著简化了连接方式(仅需4根线)。
I2C LCD1602模块特性
硬件组成:
- 核心部分:标准HD44780兼容的LCD1602显示屏
- I2C转换芯片:PCF8574T(最常用)或兼容芯片
- 接口优化:仅保留4个引脚
- VCC(+5V)
- GND
- SDA(数据线)
- SCL(时钟线)
地址配置:
PCF8574T的地址由A0-A2引脚决定:
地址 = 0x27 | (A2<<2 | A1<<1 | A0)
大多数模块默认A0/A1/A2接地,地址为0x27(备选地址0x3F)。特别说明:I2C_ADDR 0x4E是我自己试出来的。卖家产品说明里写了默认地址是0x27。
引脚映射(PCF8574T):
位 | 对应LCD引脚 | 功能 |
P0 | RS | 寄存器选择 |
P1 | RW | 读/写(通常接地) |
P2 | EN | 使能信号 |
P3 | BL | 背光控制 |
P4 | D4 | 数据位4 |
P5 | D5 | 数据位5 |
P6 | D6 | 数据位6 |
P7 | D7 | 数据位7 |
重要提示:实际连接方式可能因模块而异,需要查阅具体模块资料
PCF8574T芯片简介
芯片基本信息
PCF8574T是一款由NXP(原Philips)公司生产的8位I/O扩展芯片,通过I2C总线接口扩展微控制器的GPIO能力。其主要特点:
- 接口:标准I2C串行接口(SDA、SCL)
- 通道数:8路双向I/O口(准双向结构)
- 地址选择:3位硬件地址(最多支持8个设备)
- 工作电压:5V~6V(兼容3.3V和5V系统)
- 封装:DIP16、SO16、TSSOP16等
- 关键功能:
- 开漏中断输出(INT)
- 准双向端口(无需方向配置)
- 100kHz I2C总线传输速率
引脚功能(DIP16封装)
引脚 | 符号 | 功能描述 |
1-7 | P0-P6 | 双向I/O端口0-6 |
8 | GND | 电源地 |
9 | P7 | 双向I/O端口7 |
10 | INT | 中断输出(低电平有效) |
11 | A0 | I2C地址选择位0 |
12 | A1 | I2C地址选择位1 |
13 | A2 | I2C地址选择位2 |
14 | VCC | 电源(2.5-6V) |
15 | SDA | I2C数据线 |
16 | SCL | I2C时钟线 |
I/O端口工作原理
PCF8574T采用”准双向”端口设计:
- 输出模式:当向端口写”1″时,输出高阻抗;写”0″时输出低电平
- 输入模式:端口自动具备弱上拉功能(约100μA),无需外部上拉电阻
- 中断功能:任何输入信号变化会触发INT引脚拉低
I2C通信协议
设备地址格式
1 0 0 1 A2 A1 A0 R/W ┌──┬──┬──┬──┬──┬──┬──┬──┐ │1 │0 │0 │1 │A2│A1│A0│R/W│ └──┴──┴──┴──┴──┴──┴──┴──┘ MSB LSB
- 固定部分:1001 (0x4的前4位)
- 地址选择:A2 A1 A0可由硬件设置
- 读写控制:0-写操作,1-读操作
例如:
- A2A1A0=000时,写地址:0x40 (0100 0000)
- A2A1A0=000时,读地址:0x41 (0100 0001)
数据传输格式
(1) 写操作
S | 从机地址+W | ACK | 数据字节 | ACK | P
- 主机发送写命令后,传输一个字节(8位)数据
- 数据直接写入输出锁存器
(2) 读操作
S | 从机地址+R | ACK | [数据字节] | NACK | P
- 读取的数据为当前I/O引脚状态
- 每次读取后INT引脚自动复位
51单片机软件驱动实现
51单片机硬件连接
LCD引脚 | 51单片机引脚 | 说明 |
VCC | +5V | 电源正极 |
GND | GND | 电源地 |
SDA | P1.0 | I2C数据线 |
SCL | P1.1 | I2C时钟线 |
#include <reg52.h> #include <intrins.h> // 修复:为delay_ms添加函数声明 void delay_ms(unsigned int ms); #define I2C_ADDR 0x4E // 修复:更常见的地址值 sbit SDA = P1^0; sbit SCL = P1^1; // 增强延时函数 void I2C_Delay(void) { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } // 确保I2C信号完全符合时序要求 void I2C_Start(void) { SDA = 1; I2C_Delay(); SCL = 1; I2C_Delay(); SDA = 0; I2C_Delay(); SCL = 0; I2C_Delay(); } void I2C_Stop(void) { SDA = 0; I2C_Delay(); SCL = 1; I2C_Delay(); SDA = 1; I2C_Delay(); } bit I2C_WaitAck(void) { SDA = 1; I2C_Delay(); SCL = 1; I2C_Delay(); if(SDA) { SCL = 0; return 0; } SCL = 0; return 1; } // 发送字节函数 void I2C_SendByte(unsigned char dat) { unsigned char i; for(i = 0; i < 8; i++) { SDA = (dat & 0x80) ? 1 : 0; dat <<= 1; I2C_Delay(); SCL = 1; I2C_Delay(); SCL = 0; I2C_Delay(); } // 添加ACK检查 if(!I2C_WaitAck()) { // ACK错误处理 } } // 修复:为LCD_SendInitByte添加函数声明 void LCD_SendInitByte(unsigned char byte); // 初始化信号发送函数 void LCD_SendInitByte(unsigned char byte) { I2C_Start(); I2C_SendByte(I2C_ADDR); I2C_SendByte(byte | 0x04); // EN=1 I2C_SendByte(byte); // EN=0 I2C_Stop(); } // 完整修复的4位传输函数 void LCD_Write4bit(unsigned char data_value, unsigned char rs) { unsigned char hi_nib = data_value & 0xF0; unsigned char lo_nib = (data_value << 4) & 0xF0; // 控制位设置:RS + 背光(0x08) unsigned char control = rs | 0x08; // 发送高4位 I2C_Start(); I2C_SendByte(I2C_ADDR); I2C_SendByte(hi_nib | control | 0x04); // EN=1 I2C_SendByte(hi_nib | control); // EN=0 I2C_Stop(); // 发送低4位 I2C_Start(); I2C_SendByte(I2C_ADDR); I2C_SendByte(lo_nib | control | 0x04); // EN=1 I2C_SendByte(lo_nib | control); // EN=0 I2C_Stop(); } // 修复的LCD命令写入 void LCD_Cmd(unsigned char cmd) { LCD_Write4bit(cmd, 0x00); // RS=0 if(cmd == 0x01 || cmd == 0x02) { delay_ms(5); } else { delay_ms(2); } } // 修复的LCD数据写入 void LCD_Data(unsigned char dat) { LCD_Write4bit(dat, 0x01); // RS=1 delay_ms(2); } // 完全重写的初始化序列 void LCD_Init(void) { delay_ms(50); // 特殊初始化序列 LCD_SendInitByte(0x30); // 第一次尝试 delay_ms(5); LCD_SendInitByte(0x30); // 第二次尝试 delay_ms(1); LCD_SendInitByte(0x30); // 第三次尝试 delay_ms(1); // 进入4位模式 LCD_SendInitByte(0x20); delay_ms(1); // 4位模式初始设置 LCD_Cmd(0x28); // 4位总线,2行显示,5x8点阵 LCD_Cmd(0x0C); // 开显示,关光标,不闪烁 LCD_Cmd(0x06); // 地址增量,光标右移 LCD_Cmd(0x01); // 清屏 delay_ms(5); } // 毫秒级延时函数 void delay_ms(unsigned int ms) { unsigned int i, j; for(i = 0; i < ms; i++) for(j = 0; j < 123; j++); // 调整延时精度 } // 修复的显示字符串函数 void LCD_Print(unsigned char row, unsigned char col, char *str) { unsigned char addr = (row == 0) ? 0x80 : 0xC0; addr += col; LCD_Cmd(addr); while(*str) { LCD_Data(*str++); } } // 背光控制函数 void LCD_Backlight(unsigned char state) { unsigned char ctrl = (state) ? 0x08 : 0x00; I2C_Start(); I2C_SendByte(I2C_ADDR); I2C_SendByte(ctrl); I2C_Stop(); } // 主函数测试 void main(void) { // 初始化端口 P1 = 0xFF; // 设置P1口为高电平 LCD_Init(); LCD_Backlight(1); // 开启背光 LCD_Print(0, 0, "I2C LCD1602 OK"); LCD_Print(1, 0, "STC89C52RC"); while(1) { // 主循环 } }