news 2026/10/14 16:31:37

四针LCD1602与PCF8574T:I2C转接板驱动原理与踩坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
四针LCD1602与PCF8574T:I2C转接板驱动原理与踩坑指南

简介:一份面向51单片机开发者的LCD1602显示驱动资源,基于PCF8574T芯片实现四线I2C(IIC)通信,可将原本需要最少6根并行线的LCD1602缩减为SCL、SDA、VCC、GND共4根线,从而大幅节省单片机IO资源。压缩包共25个文件,包括C语言源码(main.c、1602_bsp.c、delay_bsp.c)、头文件、Keil工程文件(.uvproj)以及编译生成的hex、lst、obj、map等中间文件,整体大小仅55KB,非常适合初学者或项目参考。目前已有2065人浏览学习,可直接在STC89C51等常见51芯片上移植使用。资源内含LCD1602初始化序列、指令写入、数据写入及通过PCF8574T扩展GPIO的完整驱动代码,并且工程结构清晰,包含延时函数、底层驱动与主控逻辑分层,可帮助读者快速理解I2C时序和LCD控制方法,作为小型嵌入式显示模块的现成方案十分实用。

1. 四针LCD1602:为什么大家都在用PCF8574T把并行屏改成I2C

传统LCD1602要占掉至少6个GPIO,这在引脚紧张的开发板上简直是灾难。四针LCD1602方案用PCF8574T把并行接口转成I2C总线,只留VCC、GND、SDA、SCL四根线,彻底解放单片机引脚。我最早接触这个方案是在一个温湿度显示项目里,当时主控引脚被传感器和按键占满,用I2C转接板后不仅接线从十几根缩到四根,连背光控制都省了一个三极管。这个方案适合所有I2C资源富余、但GPIO捉襟见肘的场景,从51到STM32再到树莓派都能用,而且PCF8574T价格便宜,是LCD1602最常见的外设扩展方案。

很多人以为四针LCD1602是屏幕本身改了接口,其实你买到的可能只是一块普通1602加一块转接板。核心变化在驱动方式:单片机通过I2C协议向PCF8574T写入8位数据,其中高4位给数据线D4-D7,P3脚控制背光,剩下的引脚控制RS和E信号。这相当于把原来的并行时序搬到I2C总线上软模拟,理解这一点,后面的代码和踩坑就都能串起来了。


2. PCF8574T与四针接线:从I2C地址到寄存器映射

2.1 PCF8574T的I2C地址为什么会翻车

PCF8574T是带I2C接口的8位双向IO扩展芯片,通过PCA9546这类I2C开关级联也能挂多块,但最常见的用法就是直连LCD1602。这块芯片的I2C地址由A0、A1、A2三个引脚的电平决定,基地址是0x20(PCF8574T)或0x38(PCF8574AT)。问题来了——市面上很多转接板把A0-A2全部接地,所以地址固定是0x27;但PCF8574AT的基地址不同,同样全部接地时地址变成0x3F。我第一次用某厂家的转接板,扫描I2C设备发现地址是0x3F,查了芯片丝印才知道是AT后缀。所以拿到转接板第一件事是看芯片型号,然后写个I2C扫描程序确认地址,别硬套网上的驱动。

芯片后缀A0-A2全接地时地址A0-A2全接VCC时地址
PCF8574T0x270x2A
PCF8574AT0x3F0x42

如果你在驱动里发现初始化函数有lcd_addr = 0x27或lcd_addr = 0x3F两种写法,多半是芯片型号差异。我一般会在代码里留一个宏定义,换硬件时只改一个地方,不用全局搜地址。

2.2 四针接线的真实引脚定义

所谓四针LCD1602,不是屏幕只有四个引脚,而是转接板对外只引出VCC、GND、SDA、SCL。SDA和SCL要接主控的I2C引脚,VCC接5V还是3.3V取决于转接板上的电平转换电路。有些转接板带双向电平转换芯片,可以接3.3V主控;有些就是直连,必须接5V。我踩过一个坑:某开发板的I2C引脚是开漏输出,外部上拉到3.3V,转接板却是5V供电,结果SDA和SCL电平不匹配,逻辑分析仪上看波形全是乱的。后来统一把转接板供电改到主控同电压,问题消失。接线顺序建议:先接GND,再接VCC,最后接SDA和SCL——这个顺序能避免带电插拔时I2C总线上产生毛刺导致锁死。

PCF8574T的P0到P7八个引脚在转接板上通常标着对应的LCD引脚编号,默认映射是P0->RS、P1->RW(常接地)、P2->E、P3->背光,P4-P7分别接D4、D5、D6、D7。这套映射不是芯片官方规定的,是转接板厂家定的。所以买转接板时看一眼背面丝印,确认P3是背光控制,否则写背光开关时会误操作其他引脚。

2.3 寄存器操作与模拟并口时序

PCF8574T本质上是一个I2C控制的8位锁存器,写入的字节会直接出现在P0-P7引脚上。所以驱动LCD1602的关键不是操作芯片内部寄存器,而是通过I2C写字节模拟出液晶模块需要的时序。LCD1602的写命令流程是:RS置低表示写命令,E引脚拉高再拉低产生下降沿,数据在下降沿被锁存。在四针方案里,这些操作全部通过写不同字节到PCF8574T完成。比如要发送一个命令字节0x01(清屏),先将高4位0x0放到D4-D7上,同时RS为0,E为高,得到一个字节写入;再将E拉低,得到另一个字节写入。两次写入之间延时至少要1ms,清屏命令要等更久。

我在实际驱动中会把这种时序封装成lcd_write_nibble函数,先写高4位再写低4位,每次操作E引脚电平翻转。用逻辑分析仪查看时序时,能看到I2C总线上每写一个字节,对应的P2(E)引脚就出现一次脉冲,这就是模拟并口的直接证据。这个思路理解了,后面不管换什么单片机,移植驱动都很快。


3. 用代码驱动四针LCD1602:初始化流程与显示函数

3.1 初始化序列为什么是固定的

LCD1602的初始化时序不能乱来,因为芯片内部的上电状态不确定,必须用连续的几次命令把它拉回已知状态。四针模式下,I2C转接板刚上电时PCF8574T输出全为高,LCD的数据线和控制线都是高电平,此时直接发命令会失败。标准的初始化序列是:延时40ms以上等LCD内部复位,然后连续发送三次0x30(8位模式指令),再切换到4位模式发送0x20,最后设置显示参数。这套序列里的每个延时都来源LCD控制芯片HD44780的数据手册——上电后需要等待内部初始化完成,期间不能发命令。

void lcd_init(void) { delay_ms(50); // 等待LCD上电稳定 lcd_write_cmd(0x30); // 8位模式指令 delay_ms(5); lcd_write_cmd(0x30); // 重复发送,确保进入已知状态 delay_ms(1); lcd_write_cmd(0x30); delay_ms(1); lcd_write_cmd(0x20); // 切到4位模式 delay_ms(1); lcd_write_cmd(0x28); // 4位模式,2行,5x8点阵 lcd_write_cmd(0x0C); // 显示开,光标关,闪烁关 lcd_write_cmd(0x01); // 清屏 delay_ms(2); // 清屏要等2ms以上 }

这里有几个参数值得推敲。0x28是在4位模式下发送的,先发高4位0x2再发低4位0x8,如果初始化时忘了切4位模式直接发0x28,LCD会按8位模式解析,导致显示乱码。0x0C控制显示开关,如果想看到光标调试就改成0x0E,调试完再改回来。0x01清屏命令需要延时至少1.6ms,实际用2ms比较稳妥。整个初始化序列的顺序和延时都是硬性的,我见过有人把延时全删了,结果屏幕偶尔能亮偶尔白屏,最后老老实实加回来。

3.2 写字节与写命令的底层拆分

四针模式的核心函数是lcd_write_byte,它把一次8位传输拆成两个4位小段,每个小段都要带上RS和E的电平组合。这里最容易搞混的是RS电平:写命令时RS必须为0,写数据时RS必须为1。而E引脚在每个小段传输时都要产生一个下降沿,所以一共要写4个字节到PCF8574T:高4位E高、高4位E低、低4位E高、低4位E低。如果只写两个字节,E没有翻转,数据全丢。

void lcd_send_nibble(uint8_t nibble, uint8_t rs) { uint8_t value = (nibble << 4) | (rs ? 0x01 : 0x00) | 0x04; // P4-P7为数据,P0为RS,P2为E,P1保持低电平(RW=0) i2c_write_byte(PCF8574_ADDR, value); // E高,锁存数据 delay_us(1); // 保持高电平时间 i2c_write_byte(PCF8574_ADDR, value & ~0x04); // E拉低,产生下降沿 delay_us(1); } void lcd_write_cmd(uint8_t cmd) { lcd_send_nibble(cmd >> 4, 0); // 高4位,RS=0 lcd_send_nibble(cmd & 0x0F, 0); // 低4位,RS=0 delay_ms(1); // 命令执行时间 } void lcd_write_data(uint8_t data) { lcd_send_nibble(data >> 4, 1); // 高4位,RS=1 lcd_send_nibble(data & 0x0F, 1); // 低4位,RS=1 delay_us(50); // 写数据比命令快 }

上面代码里的| 0x04是把E引脚初始化为高,然后在第二个I2C写操作里通过& ~0x04把E拉低。我特意加了delay_us(1),虽然很多I2C从设备不需要这么短的延时,但LCD1602的HD44780控制器要求E高电平至少保持一定宽度,延时太短会出现偶发漏字。lcd_write_cmd里的delay_ms(1)对应命令执行时间,清屏和回家命令要更长。实际调试时如果发现显示字符偶尔错位,优先加大命令间的延时,而不是调I2C速率。

3.3 显示字符串与光标定位

有了底层写字节函数,上层的显示操作就简单了。LCD1602内部有DDRAM地址,第一行从0x00开始,第二行从0x40开始。写字符前要先发设置地址命令,注意这个命令的地址要加上0x80作为命令标志位。

void lcd_set_cursor(uint8_t row, uint8_t col) { uint8_t addr = (row == 0) ? col : (0x40 + col); lcd_write_cmd(0x80 | addr); } void lcd_print_string(const char *str) { while (*str) { lcd_write_data((uint8_t)(*str)); str++; } } void lcd_demo(void) { lcd_set_cursor(0, 0); lcd_print_string("Hello, LCD1602!"); lcd_set_cursor(1, 0); lcd_print_string("I2C via PCF8574T"); }

lcd_set_cursor里的row参数只建议传0或1,第二行地址是从0x40开始的,不信邪传2进去会写到第三行甚至DDRAM末尾,显示直接飞了。lcd_print_string每次写一个字符,每个字符都要经过高4位低4位两次传输,共四次I2C操作。如果字符串很长,I2C速率不够会出现肉眼可见的刷新延迟,比如连续显示几十个字符时要几百毫秒。实际项目中我通常只在状态变化时才刷新局部,不整屏重刷。

3.4 背光控制与I2C时序的耦合

PCF8574T的P3引脚在多数转接板上控制背光,写1打开背光,写0关闭。问题在于背光操作和LCD数据共用同一个锁存器,你在发送命令或数据时,P3的电平状态会参与组成整个字节。比如发送高4位数据时,value的计算会带上背光位,如果这个位被意外清零,背光就会灭掉。

uint8_t lcd_backlight = 0x08; // P3置高,背光开 void lcd_send_nibble(uint8_t nibble, uint8_t rs) { uint8_t value = (nibble << 4) | (rs ? 0x01 : 0x00) | 0x04 | lcd_backlight; i2c_write_byte(PCF8574_ADDR, value); delay_us(1); i2c_write_byte(PCF8574_ADDR, (value & ~0x04) | lcd_backlight); delay_us(1); } void lcd_backlight_ctrl(uint8_t on) { lcd_backlight = on ? 0x08 : 0x00; uint8_t value = lcd_backlight; i2c_write_byte(PCF8574_ADDR, value); // 仅改背光,不碰其他引脚 }

我看到网上很多驱动把背光位直接写死为0x08,没有独立变量,这样想关背光就得改函数本体。我习惯用一个全局变量lcd_backlight,所有写操作都加上它,背光开关函数只更新这个变量并写一次当前状态。有个细节:关背光时直接写0x00会把所有引脚拉到低,如果此刻LCD正处于E高电平状态,相当于产生了一个意外的下降沿,可能误触发锁存。所以我通常在关背光前保证E引脚是低电平,或者在写背光状态时保留当前E电平。


4. 四针LCD1602的5个高频踩坑:从白屏到乱码的排查手册

4.1 I2C扫描不到设备:地址问题还是接线问题

现象:执行I2C扫描程序,返回设备列表为空,或者找不到0x27/0x3F。原因可能有三:转接板供电没到位,SDA或SCL接反,或者地址就是和默认驱动不匹配。解决:先测转接板VCC引脚对地电压,确保是工作电压;然后单独跑一个I2C从设备扫描程序,把所有发现的地址打出来。如果扫描结果里有0x27以外的地址,就在驱动里改成那个地址。我还遇到过某转接板把A0跳线焊到VCC,地址变成0x2A,这种非标地址防不胜防,只信扫描结果。

// I2C扫描代码,逐个地址发探测帧 for (uint8_t addr = 0x08; addr < 0x78; addr++) { uint8_t ret = i2c_start(addr << 1); if (ret == 0) { printf("Found device at 0x%02X\n", addr); i2c_stop(); } }

4.2 白屏但有背光:初始化时序没跑对

现象:屏幕亮起,背光正常,但光标处没有任何字符。原因:LCD1602的HD44780控制器没有正确进入4位模式,或者初始化序列被I2C传输速率打乱。解决:把初始化函数里的延时全部放大2倍试试,尤其是三次0x30之间的延时。我遇到过一个现象是运行两次初始化后才正常,后来加了上电后delay_ms(100)才根治。如果你用的主控I2C速率超过100kHz,建议先用i2c_set_clock(100000)把速率降下来,四针方案对时序敏感,高速下容易丢位。

4.3 显示乱码:高4位和低4位顺序搞反

现象:屏幕上出现不认识的字符,或是想显示的“A”变成奇怪的符号。原因:这类乱码基本都是在发送高4位和低4位时顺序颠倒,或者RS电平在写数据时被置低。解决:在lcd_send_nibble里打印或逻辑分析仪抓取每次写入的字节,对照value计算方式,确认高4位确实来自cmd >> 4,低4位来自cmd & 0x0F。如果RS为0时写字符,LCD会把它当命令解析,出来的自然是乱码。调试时先把所有的字符串写操作换成单个固定字符,比如写'A',看是否正常,缩小排查范围。

4.4 第一行正常第二行乱码:DDRAM地址偏移错了

现象:第一行显示完全正确,第二行要么空白要么显示的内容和第一行一样。原因:写第二行时光标定位用了0x40 + col,但忘了在命令字节加0x80标志位。解决:检查lcd_set_cursor函数,确保地址最终拼成0x80 | addr。还有一个偷懒写法是直接把第二行地址硬编码为0xC0,第一行为0x80,这样写死虽能用,但换屏幕型号可能不兼容,还是用计算方式稳妥。

4.5 字符闪烁或背景跳动:背光P3引脚干扰数据总线

现象:显示内容正常,但背光忽明忽暗,或者字符在某些操作后消失。原因:操作背光时写入了非预期值,导致数据引脚电平变化。解决:所有写入PCF8574T的字节都要带上当前的lcd_backlight状态值,不能只写数据。我习惯在lcd_send_nibble里把背光位用变量组合进去,背光控制函数单独处理,确保数据引脚和背光引脚互不干扰。如果你发现背光跳动和字符显示同步发生,多半是这里出了问题。


5. 进阶优化:四针LCD1602的刷新提速与自定义字符

5.1 通过批量写减少I2C事务次数

每个字符要4次I2C写操作,一段文本刷下来I2C总线负载不小。可以优化的是:把lcd_write_data拆成内部直接操作,不做函数嵌套,减少函数调用开销;或者用一个缓冲数组,先拼好所有要写的字节,再一次性通过I2C写入。我实测过,在100kHz I2C速率下,逐个字节写和批量写相比,批量方式在长文本下能省掉将近一半的时间。这是因为I2C每次写操作都有起始条件、地址帧、数据帧、停止条件,拆成小段后这些开销会被放大。

void lcd_print_at(uint8_t row, uint8_t col, const char *str) { lcd_set_cursor(row, col); while (*str) { uint8_t data = (uint8_t)(*str); // 高4位带E脉冲 uint8_t v = (data >> 4) << 4 | lcd_backlight | (row ? 0x80 : 0x00); // 低4位带E脉冲 str++; } }

上面这段是示意,实际优化时我直接用数组把高4位和低4位对应的四个PCF8574值算好,然后按顺序I2C发送。注意第二行的定位命令也要正确加入,否则优化完字符全挤在第一行。这个优化对I2C总线上还挂了其他从设备的项目特别有用,能降低总线占用率,给传感器读取留出带宽。

5.2 自定义字符与复用非标准字符

LCD1602支持8个自定义字符,存储在CGRAM区域,地址从0x40到0x7F。四针模式下写CGRAM和写DDRAM走同一个通道,只是先要发一条设置CGRAM地址的命令。很多项目需要显示特殊符号,比如温度的小圆圈、进度条方块,都可以用自定义字符实现。需要注意的是,自定义字符一旦写入,掉电就没了,每次上电都要重新写一遍。

void lcd_create_char(uint8_t cgram_addr, uint8_t *bitmap) { lcd_write_cmd(0x40 | (cgram_addr << 3)); // 设置CGRAM基地址 for (uint8_t i = 0; i < 8; i++) { lcd_write_data(bitmap[i]); // 每行5位点阵 } }

cgram_addr范围0到7,对应LCD1602的8个自定义字符位置。bitmap数组每字节只用低5位,高3位忽略。写入后想显示这个自定义字符,直接往DDRAM写的位置数字0-7即可。很多人把这里搞混,以为写0x01是显示一个字符,其实它显示的是第一个自定义图形。

5.3 I2C速率与LCD时序的平衡点

四针LCD1602的I2C速率不是越高越好。太高会导致E引脚高低电平宽度不足,LCD控制器采样失败;太低则刷新慢。我调过多次,100kHz是稳定点,400kHz也能用但要保证从设备支持快速模式。PCF8574T本身支持400kHz,但LCD1602的接口时序是瓶颈。如果你在高速下遇到偶发乱码,降低到100kHz通常能解决。反过来,如果显示刷新明显偏慢,可以考虑把时钟升到200kHz并加一点延时补偿。

I2C速率E脉冲宽度稳定性适用场景
50kHz足够极稳调试排障
100kHz合适稳定默认推荐
200kHz偏窄偶发乱码需要刷新速度
400kHz危险不稳定不推荐用于LCD

我一般把100kHz作为默认值,只有确认转接板质量好才尝试更高的速率。实际项目中,如果屏幕上需要高频动态更新,比如毫秒级数值刷新,我更建议换用带并口的屏幕或用SPI接口,而不是硬扛I2C的带宽。


6. 四针LCD1602的最终选择:什么场景用转接板,什么场景直接并行

这套方案省引脚的效果立竿见影,但它不是万能的。如果你的主控只剩一个I2C接口且还要挂多个传感器,又或者刷新率要求很高,并行接口仍是更好的选择。我做过一个测试台,每秒要更新屏幕上的多个参数,I2C方案的刷新率只能到大概几十赫兹,并行方案能到上百赫兹。反过来说,如果你的项目是数据采集器、小仪表盘、家庭自动化面板这类低频刷新场景,四针LCD1602绝对是首选。它省下的GPIO可以扩展按键矩阵、LED指示灯,或者干脆留给通信模块。从成本看,转接板比裸屏贵不了多少,但布线难度和故障排查成本降了一个量级。

我个人的习惯是:在Arduino、ESP32、STM32这些I2C资源充足的主控上,直接用四针方案;遇到引脚真的多到用不完的场合,比如某开发板自带并口LCD接口,才考虑直连。四针方案的电路结构简单,故障点多集中在连接器接触不良,只要焊接过关、地址确认无误,基本不会出问题。这句话不是玄学,是我焊过几十块转接板后的真实体感。

最后分享一个我踩了两次的教训:换主控平台时,驱动代码里的I2C读操作要特别小心,PCF8574T是准双向IO,读引脚前要先写1。我曾在某平台上用标准I2C库读转接板引脚,结果读回来的数据全是0,花了大半天排查发现是读流程里少了上拉写操作。这类经验记不住,只能靠多掉坑。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 16:31:31

CSP-S 2023 真题 种树 题解(提高+/省选-)|check 函数怎么写

今天看 CSP-S 2023 的这道题&#xff0c;考点是二分答案。 题目来源&#xff1a;ACGO A7095 &#xff5c; 洛谷 P9755难度定位&#xff1a;提高/省选- &#xff5c; 通过率&#xff1a;50.00% 一、题目信息 题目名称&#xff1a;种树 所属赛事&#xff1a;2023 年 CSP-S 组复赛…

作者头像 李华
网站建设 2026/10/14 16:31:04

抗量子签名与分层共识:把公链的安全和吞吐一起升级

量子计算这几年被反复拿出来讲&#xff0c;但大部分讨论都停在“比特币要被破解了”的层面上&#xff0c;很少有人去聊区块链项目到底怎么应对。这周把某项目公开资料里的两个核心设计认真看完&#xff0c;一个是抗量子hash算法&#xff0c;一个是分层共识机制&#xff0c;后者…

作者头像 李华
网站建设 2026/10/14 16:30:16

Docker端口映射原理与阿里云ECS实战排障指南

1. 为什么端口映射是 Docker 上云落地的第一道门槛“以阿里云服务器为例理解 Docker 端口映射”——这个标题看似平实&#xff0c;但背后藏着大量新手在真实生产环境里反复踩坑、查文档、重启容器、抓包调试&#xff0c;最后才恍然大悟的痛感。我带过不少刚从本地开发环境切到云…

作者头像 李华
网站建设 2026/10/14 16:30:15

用Home Assistant把密室逃脱谜题变成可编程设备

做密室逃脱的人大概都幻想过这么一件事&#xff1a;如果把整个房间变成一套能随时读状态、随时触发、随时重置的逻辑系统&#xff0c;现场把控就会轻松很多。标题里那句“Turn every escape room puzzle into a Home Assistant device”&#xff0c;我最早看到时愣了一下&#…

作者头像 李华
网站建设 2026/10/14 16:30:00

6万+月薪!AI岗位暴涨400%?真相是:两极分化!普通人如何精准入行?高薪赛道+进阶路线全解析!

本文揭秘了AI行业高薪岗位背后的真相&#xff1a;薪资分布不均&#xff0c;顶尖岗位月薪超百万&#xff0c;但行业两极分化严重。文章详细分析了2026年AI岗位薪酬地图&#xff0c;涵盖算法、应用、工程等岗位&#xff0c;揭示了城市、企业类型对薪酬的影响。重点介绍了AI Agent…

作者头像 李华