为什么需要 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 瓶颈!