STM32引脚不够用?试试用PCF8574芯片扩展IO口(附完整I2C驱动代码) 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时钟引脚如PB6I2C数据线(SDA)连接STM32的I2C数据引脚如PB7中断线(INT)可选连接STM32的外部中断引脚电源线(VCC/GND)连接3.3V或5V电源典型连接电路如下--------------- | STM32 | | | | PB6----SCL | | PB7----SDA | | PA0----INT | -------|------- | -------|------- | PCF8574 | | A0-A2GND | | P0-P7--LEDs | ---------------2.2 地址配置技巧PCF8574通过A0-A2引脚可以设置不同的设备地址允许在同一条I2C总线上挂载最多8个芯片// PCF8574基础地址为0x40A0-A2接地时 #define PCF8574_ADDR 0x40 // 若A0接VCCA1-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_ADDR1, 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_ADDR1)|0x01, temp, 1, 100); return temp; } void PCF8574_WriteOneByte(uint8_t Data) { HAL_I2C_Master_Transmit(hi2c1, PCF8574_ADDR1, Data, 1, 100); } void PCF8574_WriteBit(uint8_t bit, uint8_t sta) { uint8_t data PCF8574_ReadOneByte(); if(sta) data | (1bit); else data ~(1bit); PCF8574_WriteOneByte(data); } uint8_t PCF8574_ReadBit(uint8_t bit) { return (PCF8574_ReadOneByte() (1bit)) ? 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 i0; i8; 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 c0; c4; c) { PCF8574_WriteOneByte(~(1col_pins[c])); for(int r0; r4; 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的中断唤醒这种设计可使平均电流降至微安级别。