STM32引脚不够用?试试用PCF8574芯片扩展IO口(附完整I2C驱动代码)
在STM32开发过程中,IO口资源不足是许多开发者都会遇到的痛点。无论是智能家居项目中的多路传感器采集,还是工业控制中的复杂设备联动,有限的MCU引脚往往成为制约项目扩展的瓶颈。本文将详细介绍如何利用PCF8574这款经典的I2C接口IO扩展芯片,轻松为STM32增加8个可编程IO口,并提供可直接复用的驱动代码和硬件连接方案。
1. PCF8574芯片核心特性与应用场景
PCF8574是NXP推出的一款I2C总线接口的8位IO扩展芯片,它通过简单的2线制I2C接口,就能为微控制器扩展出8个准双向IO口。这款芯片特别适合以下应用场景:
- LED矩阵控制:驱动多个LED指示灯或数码管
- 按键扫描:扩展矩阵键盘输入接口
- 传感器集群:连接多个数字传感器(如温湿度、光照等)
- 继电器控制:管理多路继电器开关
PCF8574的主要技术参数:
| 特性 | 参数值 |
|---|---|
| 工作电压 | 2.5V-6V |
| 最大输出电流 | 25mA/引脚 |
| I2C时钟频率 | 最高400kHz |
| 待机电流 | <10μA |
| 可寻址设备数 | 8个(通过A0-A2引脚) |
提示:PCF8574的准双向IO口设计意味着它不需要额外配置输入/输出方向,简化了编程接口。
2. 硬件连接与电路设计
2.1 基本连接原理图
PCF8574与STM32的连接非常简单,只需要4根线:
- I2C时钟线(SCL):连接STM32的I2C时钟引脚(如PB6)
- I2C数据线(SDA):连接STM32的I2C数据引脚(如PB7)
- 中断线(INT):可选,连接STM32的外部中断引脚
- 电源线(VCC/GND):连接3.3V或5V电源
典型连接电路如下:
+---------------+ | STM32 | | | | PB6----SCL | | PB7----SDA | | PA0----INT | +-------|-------+ | +-------|-------+ | PCF8574 | | A0-A2=GND | | P0-P7--LEDs | +---------------+2.2 地址配置技巧
PCF8574通过A0-A2引脚可以设置不同的设备地址,允许在同一条I2C总线上挂载最多8个芯片:
// PCF8574基础地址为0x40,A0-A2接地时: #define PCF8574_ADDR 0x40 // 若A0接VCC,A1-A2接地: #define PCF8574_ADDR 0x423. 完整驱动代码实现
3.1 初始化与基本读写
首先需要实现I2C底层驱动,以下是基于HAL库的PCF8574驱动代码:
// pcf8574.h #ifndef __PCF8574_H #define __PCF8574_H #include "stm32f1xx_hal.h" #define PCF8574_ADDR 0x40 uint8_t PCF8574_Init(void); uint8_t PCF8574_ReadOneByte(void); void PCF8574_WriteOneByte(uint8_t Data); void PCF8574_WriteBit(uint8_t bit, uint8_t sta); uint8_t PCF8574_ReadBit(uint8_t bit); #endif// pcf8574.c #include "pcf8574.h" #include "i2c.h" uint8_t PCF8574_Init(void) { uint8_t temp = 0; HAL_Delay(100); // 尝试读取设备是否响应 if(HAL_I2C_IsDeviceReady(&hi2c1, PCF8574_ADDR<<1, 3, 100) != HAL_OK) { return 1; // 设备未响应 } // 初始状态所有IO输出高电平 PCF8574_WriteOneByte(0xFF); return 0; } uint8_t PCF8574_ReadOneByte(void) { uint8_t temp; HAL_I2C_Master_Receive(&hi2c1, (PCF8574_ADDR<<1)|0x01, &temp, 1, 100); return temp; } void PCF8574_WriteOneByte(uint8_t Data) { HAL_I2C_Master_Transmit(&hi2c1, PCF8574_ADDR<<1, &Data, 1, 100); } void PCF8574_WriteBit(uint8_t bit, uint8_t sta) { uint8_t data = PCF8574_ReadOneByte(); if(sta) data |= (1<<bit); else data &= ~(1<<bit); PCF8574_WriteOneByte(data); } uint8_t PCF8574_ReadBit(uint8_t bit) { return (PCF8574_ReadOneByte() & (1<<bit)) ? 1 : 0; }3.2 中断功能实现
PCF8574的中断功能可以大幅降低MCU的轮询开销:
// 配置中断引脚(以PA0为例) void PCF8574_INT_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI0_IRQn); } // 中断服务例程 void EXTI0_IRQHandler(void) { if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) { uint8_t io_state = PCF8574_ReadOneByte(); // 读取会清除中断 // 处理IO状态变化 __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); } }4. 实战应用案例
4.1 多路LED控制
假设我们需要控制8个LED指示灯,但STM32的IO口已经所剩无几:
// 定义LED连接位置 #define LED0 0 #define LED1 1 // ... 其他LED定义 void LED_Test(void) { PCF8574_Init(); while(1) { for(int i=0; i<8; i++) { PCF8574_WriteBit(i, 1); // LED亮 HAL_Delay(200); PCF8574_WriteBit(i, 0); // LED灭 } } }4.2 矩阵键盘扫描
利用PCF8574实现4x4矩阵键盘:
uint8_t Key_Scan(void) { static const uint8_t row_pins[] = {0,1,2,3}; static const uint8_t col_pins[] = {4,5,6,7}; uint8_t key = 0xFF; // 设置列为输出,行为输入 for(int c=0; c<4; c++) { PCF8574_WriteOneByte(~(1<<col_pins[c])); for(int r=0; r<4; r++) { if(PCF8574_ReadBit(row_pins[r]) == 0) { key = r*4 + c; while(PCF8574_ReadBit(row_pins[r]) == 0); // 等待释放 break; } } PCF8574_WriteOneByte(0xFF); // 所有列高 if(key != 0xFF) break; } return key; }5. 性能优化与常见问题
5.1 I2C通信速率优化
默认情况下,STM32的I2C工作在标准模式(100kHz),我们可以提升到快速模式(400kHz):
// 在I2C初始化代码中修改时钟配置 hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; // 400kHz hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;5.2 典型问题排查
设备无响应:
- 检查I2C地址是否正确(用逻辑分析仪抓取波形)
- 确认上拉电阻已连接(通常4.7kΩ)
- 验证电源电压是否在2.5-6V范围内
中断不触发:
- 确保INT引脚已正确配置为下降沿触发
- 每次中断后必须进行读/写操作才能清除中断
输出驱动能力不足:
- PCF8574每个引脚最大驱动电流25mA
- 驱动大电流负载时建议增加晶体管或MOSFET
注意:多个PCF8574共用I2C总线时,总电容不能超过400pF,长距离传输时需要降低时钟频率。
在实际项目中,我发现合理利用PCF8574的中断功能可以大幅降低系统功耗。例如在电池供电的传感器节点中,可以让STM32进入低功耗模式,仅通过PCF8574的中断唤醒,这种设计可使平均电流降至微安级别。