为什么需要 IO 扩展?
嵌入式开发中,MCU 的 GPIO 数量往往受限于封装和成本,尤其是在驱动多个 LED、按键、继电器或读取拨码开关时,引脚资源瞬间告急。I2C 总线仅需 SDA 和 SCL 两根线,即可挂载多个外设,而 PCF8574 作为 I2C 转 8 位准双向 IO 的经典芯片,成为嵌入式 IO 扩展的首选方案。
PCF8574 核心原理
PCF8574 内部包含一个 I2C 从机接口、一个 8 位准双向 IO 端口(P0~P7)以及一个输出锁存器。其工作模式如下:
- 输出模式:向器件写入一个字节,该字节被锁存到输出端口,驱动外部负载(如 LED、继电器)。
- 输入模式:先向器件写入 0xFF(使所有引脚为高电平),然后读取端口,即可获得外部电平状态(如按键按下为低电平)。
- 准双向特性:每个引脚内部有上拉电阻,输出高电平时可驱动微弱负载,输出低电平时可吸收较大电流(如灌电流驱动 LED)。
I2C 地址:PCF8574 的地址由 A0~A2 引脚决定,默认地址为 0x20(若 A0~A2 均接地)。地址格式为 0100 A2 A1 A0,因此可挂载 8 个器件,扩展 64 路 IO。
硬件连接
以 STM32F103 为例,连接如下:
- VCC → 3.3V(或 5V,注意电平匹配)
- GND → GND
- SDA → PB7(I2C1 数据线)
- SCL → PB6(I2C1 时钟线)
- A0~A2 → GND(地址 0x20)
- P0~P7 → 外设(如 LED 串联 220Ω 电阻到 VCC,或按键一端接 GND)
注意:若 MCU 为 3.3V,PCF8574 可工作于 2.5~6V,但 I2C 上拉电阻需接至 MCU 的 VCC,防止电平冲突。
驱动代码实现(以 STM32 HAL 库为例)
1. I2C 初始化
使用 STM32CubeMX 配置 I2C1,速率 100kHz(标准模式),然后生成代码。
2. PCF8574 基本读写函数
#include "i2c.h"
#define PCF8574_ADDR 0x20 // 默认地址,注意左移一位为 0x40
// 写 8 位数据到 PCF8574
void PCF8574_Write(uint8_t data) {
HAL_I2C_Master_Transmit(&hi2c1, (PCF8574_ADDR << 1) | 0, &data, 1, 100);
}
// 从 PCF8574 读取 8 位数据
uint8_t PCF8574_Read(void) {
uint8_t data = 0;
HAL_I2C_Master_Receive(&hi2c1, (PCF8574_ADDR << 1) | 1, &data, 1, 100);
return data;
}
// 设置某一位输出高/低电平
void PCF8574_SetPin(uint8_t pin, uint8_t level) {
static uint8_t current = 0xFF; // 保存当前输出状态
if (level) {
current |= (1 << pin);
} else {
current &= ~(1 << pin);
}
PCF8574_Write(current);
}
3. 应用示例:控制 LED 和读取按键
// 初始化:所有引脚输出高电平(LED 熄灭,因为 LED 接 VCC,低电平点亮)
void PCF8574_Init(void) {
PCF8574_Write(0xFF);
}
// 点亮 P0 上的 LED(低电平有效)
void LED_On(void) {
PCF8574_SetPin(0, 0);
}
// 读取 P4 上的按键(按下为低电平)
uint8_t Key_Read(void) {
uint8_t val = PCF8574_Read();
return (val & (1 << 4)) ? 1 : 0; // 返回 1 表示未按下,0 表示按下
}
// 主循环示例
void main_loop(void) {
PCF8574_Init();
while (1) {
if (Key_Read() == 0) {
LED_On();
} else {
PCF8574_SetPin(0, 1); // 熄灭
}
HAL_Delay(10);
}
}
注意事项与常见坑
-
地址左移:HAL 库的
HAL_I2C_Master_Transmit需要 8 位地址,其中最低位为读写位,因此必须将 7 位地址左移一位。例如 0x20 左移后为 0x40(写),0x41(读)。 - 准双向 IO 的输入模式:读取前必须先写 0xFF,否则引脚可能输出低电平,导致读到的电平不准确。
- 上拉电阻:PCF8574 内部有弱上拉(约 100μA),但驱动 LED 时建议使用灌电流方式(LED 接 VCC,引脚输出低电平点亮),避免高电平驱动能力不足。
- 电平匹配:若 MCU 为 5V,PCF8574 可直接供电 5V;若 MCU 为 3.3V,建议 PCF8574 也使用 3.3V,并确保 I2C 上拉电阻接至 3.3V。
- 速度限制:I2C 标准模式 100kHz 下,切换 IO 的速率有限,不适合高频 PWM 信号,但用于按键、LED 等低速场景绰绰有余。
- 多器件扩展:通过 A0~A2 可挂载多个 PCF8574,但需确保每个地址唯一,且 I2C 总线长度和电容符合规范。
总结
PCF8574 是嵌入式 IO 扩展的经典方案,仅需两根线即可获得 8 路 IO,且代码简单、成本低廉。通过本文的驱动示例,你可以快速集成到自己的项目中。记住关键点:地址左移、输入前写 1、灌电流驱动,即可避免大部分问题。希望这篇指南能帮你轻松突破 IO 瓶颈!