STC15单片机实战:用PCF8574驱动LCD1602,告别繁琐的8位并行接线
在嵌入式开发中,LCD1602液晶屏因其价格低廉、使用简单而广受欢迎。然而传统的8位并行驱动方式需要占用大量IO口资源,对于引脚资源有限的STC15系列单片机来说尤为不便。本文将介绍如何通过PCF8574 IIC扩展芯片,仅用两根线实现LCD1602的驱动控制,大幅简化硬件连接。
1. 硬件设计与连接
1.1 核心器件选型
本次方案采用以下关键器件组合:
- STC15W4K56S4:增强型51内核单片机,运行频率可达35MHz
- PCF8574:Philips推出的IIC接口8位IO扩展芯片
- LCD1602:标准16x2字符型液晶模块(兼容HD44780控制器)
相比传统方案,这套组合具有三大优势:
- 引脚节约:从8+3根线减少到仅需2根(SCL+SDA)
- 布线简化:IIC总线支持多设备并联,扩展性强
- 代码复用:相同逻辑可适配其他IIC外设
1.2 电路连接要点
具体接线方式如下表所示:
| PCF8574引脚 | LCD1602引脚 | 功能说明 |
|---|---|---|
| P0 | RS | 寄存器选择 |
| P1 | RW | 读写控制 |
| P2 | E | 使能信号 |
| P4-P7 | DB4-DB7 | 数据线(4位模式) |
实际连接时需注意:
务必在IIC总线上添加4.7kΩ上拉电阻(SCL和SDA各一个) LCD1602的V0引脚接10kΩ电位器用于调节对比度
2. STC15特有的时序调整
2.1 与传统51的差异
STC15单片机虽然兼容传统51指令集,但由于采用增强型内核,其指令执行速度比标准51快8-12倍。这意味着直接移植传统51的延时函数会导致时序错误。
典型差异对比:
// 标准51的5us延时(@12MHz) void Delay() { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } // STC15的5us延时(@12MHz) void Delay() { unsigned char i; _nop_(); _nop_(); i = 12; while(--i); }2.2 精确延时实现
针对IIC通信和LCD1602操作,我们需要实现两种精度的延时:
- 微秒级延时:用于IIC信号建立时间
void IIC_Delay(unsigned char t) { while(t--) { _nop_(); _nop_(); _nop_(); } }- 毫秒级延时:用于LCD1602指令执行
void LCD_Delay(unsigned int ms) { unsigned int i, j; for(i=0; i<ms; i++) for(j=0; j<850; j++); // STC15@12MHz }3. IIC通信协议实现
3.1 基础通信函数
完整的IIC驱动需要实现以下基本操作:
- 起始条件:SCL高电平时SDA从高到低跳变
void IIC_Start() { SDA = 1; SCL = 1; IIC_Delay(5); SDA = 0; IIC_Delay(5); SCL = 0; }- 停止条件:SCL高电平时SDA从低到高跳变
void IIC_Stop() { SDA = 0; SCL = 1; IIC_Delay(5); SDA = 1; IIC_Delay(5); }- 字节传输:每个时钟周期传输1位数据
bit IIC_Write_Byte(unsigned char dat) { unsigned char i; bit ack; for(i=0; i<8; i++) { SDA = (dat & 0x80) ? 1 : 0; SCL = 1; IIC_Delay(5); SCL = 0; dat <<= 1; } SDA = 1; // 释放总线等待ACK SCL = 1; IIC_Delay(5); ack = !SDA; // 读取ACK信号 SCL = 0; return ack; }3.2 PCF8574特殊处理
PCF8574作为IIC从设备,有几点需要特别注意:
- 设备地址:基础地址0x40(A2A1A0接地时)
- 字节格式:每个IO对应一个位,需按LCD1602要求组合
- 响应时间:写入后需要保持足够的时间
典型写操作序列:
- 发送起始条件
- 发送设备地址+写标志(0x40)
- 发送控制字节(RS/RW/E组合)
- 发送数据字节(高4位有效)
- 发送停止条件
4. LCD1602驱动实现
4.1 4位模式初始化
LCD1602在4位模式下需要特殊的初始化序列:
void LCD_Init() { LCD_Delay(50); // 上电延时 // 特殊初始化序列 LCD_Write_Cmd(0x33); LCD_Delay(5); LCD_Write_Cmd(0x32); LCD_Delay(5); // 标准初始化 LCD_Write_Cmd(0x28); // 4位模式,2行显示 LCD_Delay(5); LCD_Write_Cmd(0x0C); // 显示开,光标关 LCD_Delay(5); LCD_Write_Cmd(0x06); // 地址自动递增 LCD_Delay(5); LCD_Write_Cmd(0x01); // 清屏 LCD_Delay(5); }4.2 数据/命令写入函数
由于采用PCF8574中转,需要将4位数据分两次传输:
void LCD_Write_4bits(unsigned char data, unsigned char control) { unsigned char hi = data & 0xF0; unsigned char lo = (data << 4) & 0xF0; // 高4位传输 IIC_Write_PCF8574(hi | control | 0x04); // E=1 IIC_Write_PCF8574(hi | control); // E=0 // 低4位传输 IIC_Write_PCF8574(lo | control | 0x04); // E=1 IIC_Write_PCF8574(lo | control); // E=0 }4.3 实用功能封装
基于底层函数,可以封装更易用的高级功能:
// 设置光标位置 void LCD_SetCursor(unsigned char row, unsigned char col) { unsigned char address = (row == 0) ? 0x80 : 0xC0; LCD_Write_Cmd(address + col); } // 显示字符串 void LCD_Print(char *str) { while(*str) { LCD_Write_Data(*str++); } }5. Proteus仿真技巧
5.1 关键仿真设置
在Proteus中仿真时需要注意:
器件模型:
- 使用"PCF8574"模型而非通用IIC器件
- LCD1602选择带HD44780控制器的型号
调试工具:
- 添加IIC Debugger监视总线通信
- 使用Logic Analyzer抓取时序波形
常见问题:
- 如果LCD不显示,检查对比度调节电压
- 通信失败时,检查上拉电阻是否添加
5.2 调试技巧分享
在实际项目中总结的调试经验:
波形对比法:将实际波形与标准IIC时序图对比
分段测试法:
- 先验证IIC基础通信
- 再测试PCF8574单独工作
- 最后整合LCD驱动
代码插桩法:在关键位置添加状态指示
void IIC_Write_PCF8574(unsigned char data) { P1 = data; // 用LED显示当前状态 // ...正常通信代码... }6. 项目实战优化建议
6.1 代码结构优化
建议采用模块化编程,分为以下文件:
- i2c.h/c:IIC基础通信
- pcf8574.h/c:PCF8574专用驱动
- lcd1602.h/c:LCD高层封装
- main.c:应用逻辑
6.2 性能提升技巧
- 延时优化:根据实际时钟调整延时参数
- 批量写入:对连续显示内容优化传输流程
- 状态缓存:减少不必要的重复指令
6.3 扩展应用思路
基于此方案可进一步扩展:
- 多设备共享:在同一条IIC总线上挂接多个PCF8574
- 按键扫描:利用剩余IO实现矩阵键盘
- 参数存储:增加AT24C02等IIC存储器
在最近的一个智能家居项目中,这套方案成功实现了仅用4个IO口(IIC+2个按键)就完成了LCD显示、按键输入和环境监测等多项功能,相比传统方案节省了12个IO口资源。