器→工具, 电子电路

51单片机学习之LCD1602

钱魏Way · · 20 次浏览
!文章内容如有错误或排版问题,请提交反馈,非常感谢!

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) {
        // 主循环
    }
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注