1. 项目概述为什么我们需要I2C I/O扩展器在嵌入式项目里尤其是用单片机比如STM32、Arduino、ESP32做核心控制的时候GPIO通用输入输出引脚不够用是个老生常谈的“痛点”。你可能遇到过这样的场景想驱动一个8x8的LED点阵需要64个引脚或者想接一堆按键、传感器主控芯片的引脚瞬间告急。这时候飞线、加锁存器、用移位寄存器方法很多但要么布线复杂要么编程繁琐。I2C总线协议的出现为这个问题提供了一个极其优雅的解决方案。它只需要两根线SDA数据线和SCL时钟线就能在总线上挂载多个设备通过唯一的地址进行通信。基于I2C的I/O扩展芯片比如我们今天要深入聊的PCA8574和PCA8574A就是这种思路下的经典产物。它们本质上是一个“翻译官”把主控通过I2C发送的指令转换成对8个独立GPIO引脚的控制反过来也能把这8个引脚的状态通过I2C汇报给主控。这枚小小的芯片其价值远不止“多出8个IO口”那么简单。首先它极大地简化了PCB布局从主控到扩展芯片只需要两根线而不是8根甚至更多。其次它支持中断INT功能这意味着你的主控不需要不停地轮询Polling这些IO口的状态而是当有按键按下、传感器信号变化时扩展芯片会主动通过中断线“喊”主控来处理大大节省了CPU资源降低了系统功耗非常适合电池供电的移动设备。最后它的每个IO口都能提供高达25mA的电流吸收Sink能力可以直接驱动LED省去了额外的驱动电路。所以无论你是想做一个复杂的仪表盘、一个多路输入的工控板还是一个需要响应大量外部事件的低功耗设备深入理解并用好PCA8574/74A这类I/O扩展器都能让你的设计事半功倍。接下来我们就抛开数据手册的官方表述从实际应用的角度把这颗芯片里里外外讲透。2. 芯片核心架构与功能拆解2.1 准双向端口Quasi-bidirectional I/O它到底“准”在哪数据手册里反复提到“Quasi-bidirectional I/O”这是PCA8574系列的核心特性也是它区别于传统MCU GPIO或其它扩展芯片如使用方向寄存器的MCP23017的关键。你可以把它想象成一个自带弱上拉电阻约100µA电流源的IO口但这个上拉是“智能”且“节能”的。作为输入时你只需要通过I2C向这个端口写一个逻辑‘1’。此时芯片内部会断开强下拉MOS管仅由内部的100µA弱电流源将引脚电位拉高。当外部信号比如一个按键接地将引脚拉低时它能轻易地克服这个微弱的电流使引脚变为低电平。主控读取时就能得到‘0’。关键点作为输入你必须先将其设置为‘1’高电平否则内部强下拉管会导通外部根本无法将其拉高。作为输出高电平时你同样写入‘1’。此时在I2C通信的应答ACK时钟周期内芯片会短暂开启一个更强的“加速上拉”电流约1mA快速将引脚电位拉到接近VDD。之后就恢复到仅靠100µA弱电流源维持高电平。这种设计是为了在驱动容性负载时获得更快的上升沿同时避免如果外部也在驱动此引脚为低时产生总线竞争和过大电流。作为输出低电平时你写入‘0’。此时芯片内部的强下拉NMOS管会持续导通将引脚牢牢地拉到接近VSS地的低电平并能吸收高达25mA的电流。重要警告如果你将此引脚配置为输出低而外部电路比如上拉电阻试图将其拉高将会产生从VDD到VSS的短路大电流可能损坏芯片。因此在将某个引脚用作输出前务必确保外部电路不会与之冲突。实操心得 准双向口的优势在于“简单”。它不需要像标准双向口那样先配置方向寄存器Input/Output再操作数据寄存器。你只需要操作一个寄存器写‘1’就是输入或输出高由外部决定写‘0’就是输出低。这种简化在资源紧张的嵌入式系统中很受欢迎。但它的缺点也很明显输出高电平的驱动能力很弱仅靠100µA维持如果后面接的负载较重如需要快速开关的MOS管栅极高电平可能会被拉低此时必须在外部增加一个上拉电阻。2.2 中断INT功能从轮询到事件驱动的飞跃中断功能是PCA8574的另一个杀手锏。INT引脚是一个开漏输出Open-Drain需要外接上拉电阻到VDD。工作原理芯片内部有一个“输入端口状态锁存器”。上电或每次主控读写操作后这个锁存器会记录当前8个IO口的逻辑状态。之后芯片会持续比较IO口的实际电压和锁存器里记录的状态。只要有任何一位的实际电平与锁存值不同INT引脚就会被拉低产生一个低有效的中断信号。中断的清除中断不会自动清除。主控MCU在检测到INT变低后必须通过I2C总线对PCA8574执行一次读操作或写操作。在操作成功的瞬间具体是在应答位或读写脉冲的上升沿/下降沿详见时序图芯片会更新内部锁存器为当前端口状态INT引脚随之被释放变回高电平。上电时的中断上电复位POR后所有端口默认状态为‘1’高电平。如果某个端口被外部电路如按键拉低了那么一上电INT就会立即变为低电平通知主控有“事件”发生。这在系统初始化时需要特别注意处理。避坑指南中断丢失Glitch如果外部输入信号在MCU读写操作期间发生跳变这个跳变可能无法触发中断或者产生一个极短的中断脉冲而被MCU错过。在设计对边沿敏感的电路如旋转编码器时需要在软件上做去抖和状态保持。开漏输出INT引脚必须接上拉电阻通常4.7kΩ - 10kΩ否则无法输出高电平。多设备中断共享可以将多个PCA8574的INT引脚通过一个线与Wire-AND的方式连接到MCU的同一个外部中断引脚都需要上拉。当任何一个设备产生中断线都会被拉低。MCU中断响应后需要依次查询每个设备的端口状态来确定是哪个设备触发了中断。查询顺序可以采用优先级或轮询方式。2.3 设备寻址如何在一片总线上挂16个芯片I2C设备靠7位地址区分。PCA8574和PCA8574A是同一颗芯片但固定地址部分不同PCA8574的固定地址是0100(二进制)。PCA8574A的固定地址是0111(二进制)。每个芯片还有三个硬件地址引脚A2, A1, A0。这三个引脚可以接VDD高电平或VSS低电平从而在固定地址后添加3位可编程地址。因此完整的7位地址构成如下[固定部分(4位)][A2][A1][A0]地址计算示例 假设我们使用一个PCA8574并将它的 A21 (VDD), A10 (VSS), A01 (VDD)。 那么其7位地址为0100101(二进制) 0x4A(十六进制写地址)。读地址为0x4B(写地址1)。由于PCA8574和PCA8574A的固定地址不同你可以在同一条I2C总线上混合使用它们。最多可以挂载8个(PCA8574) 8个(PCA8574A) 16个设备。这相当于用两根I2C线扩展出了 16 * 8 128个可控制的IO口硬件连接注意地址引脚A2/A1/A0内部没有上拉或下拉电阻。你必须通过电阻或直接连接到VDD/VSS来明确其电平悬空会导致地址不确定通信失败。3. 实战应用从电路设计到代码驱动3.1 典型应用电路设计让我们设计一个常见的应用用一颗PCA8574连接4个独立按键输入和4个LED输出。电路连接图要点电源与地VDD接系统电源2.3V-5.5VVSS接地。建议在芯片VDD和VSS引脚附近放置一个0.1µF的陶瓷去耦电容。I2C总线SCL和SDA线分别连接MCU的I2C时钟和数据线。必须在总线上拉电阻阻值根据总线速度和布线长度选择通常在4.7kΩ到10kΩ之间。如果总线较长或设备较多可以适当减小阻值。地址设置将A2, A1, A0全部接地设定此芯片地址为0x20(PCA8574, 写地址)。中断引脚INT引脚连接MCU的一个具有外部中断功能的GPIO如STM32的EXTI。必须接一个上拉电阻如10kΩ到VDD。输入端口P0-P3每个端口接一个按键到地。由于端口内部有弱上拉不需要外接上拉电阻。按键按下时端口被拉低松开时内部弱上拉将其恢复为高。输出端口P4-P7每个端口通过一个限流电阻例如220Ω - 1kΩ根据LED工作电流计算连接一个LED的阳极LED阴极接地。当端口输出‘0’低电平时电流从VDD流经电阻、LED进入芯片端口被吸收到地LED点亮。输出‘1’时端口为高阻弱上拉状态LED两端电压差很小熄灭。关键计算LED限流电阻假设系统电压VDD5VLED正向压降Vf2.0V期望工作电流If10mA。 PCA8574在输出低电平VOL(max)时引脚电压约为0.5V。 则限流电阻 R (VDD - Vf - VOL) / If (5 - 2.0 - 0.5) / 0.01 250Ω。 可以选择270Ω的标准电阻此时实际电流约为 (5-2.0-0.5)/270 ≈ 9.3mA在安全范围内。3.2 软件驱动与通信流程驱动PCA8574的本质就是标准的I2C读写。下面以STM32 HAL库为例展示关键操作。初始化// 1. 初始化I2C外设略 // 2. 初始化连接INT引脚的GPIO为外部中断输入模式下降沿触发略 // 3. 初始化PCA8574将所有端口设为输入即写入0xFF uint8_t data 0xFF; HAL_I2C_Master_Transmit(hi2c1, 0x20 1, data, 1, HAL_MAX_DELAY); // 0x20是7位地址左移1位是因为HAL库函数要求传入的地址是7位地址1。 // 此操作后P0-P3按键为输入P4-P7LED为高电平熄灭。中断服务例程ISR中的处理void EXTI0_IRQHandler(void) { // 假设INT接在EXTI0 if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) ! RESET) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); // 清除中断标志 uint8_t port_state; // 读取当前8个端口的状态这个操作会清除INT中断 if(HAL_I2C_Master_Receive(hi2c1, (0x20 1) | 0x01, port_state, 1, HAL_MAX_DELAY) HAL_OK) { // 分析port_state // 低4位P0-P3是按键状态0表示按下 // 高4位P4-P7是LED当前输出状态我们之前设置的 uint8_t key_val ~port_state 0x0F; // 取反使按下为1方便判断 if(key_val) { // 有按键按下 // 示例按键0(P0)按下则点亮LED0(P4) if(key_val 0x01) { // 将P4置0点亮LED同时保持其他端口状态不变 uint8_t new_output port_state 0xEF; // 清除P4的位置0 HAL_I2C_Master_Transmit(hi2c1, 0x20 1, new_output, 1, HAL_MAX_DELAY); } // 可以处理其他按键... } } } }关键时序理解对照数据手册图8、9写模式主控发送 START - 7位从机地址写位(0) - 从机ACK - 数据字节 - 从机ACK - STOP。数据字节在ACK之后立即出现在IO端口上。读模式主控发送 START - 7位从机地址读位(1) - 从机ACK - 主控接收数据字节 - 主控发送NACK停止读取或ACK继续读下一个字节- STOP。读操作会清除中断。中断时序当输入端口变化到与内部锁存值不同时INT变低。在读写操作的特定时钟边沿如读操作中地址字节的ACK上升沿锁存器更新INT复位变高。3.3 驱动大电流负载与并联使用每个IO口最大可吸收25mA电流整个芯片所有IO口的总电流不能超过200mA。如果需要驱动一个需要50mA的继电器线圈怎么办方案端口并联。 可以将P0和P1两个引脚并联在一起共同驱动一个负载。这样最大吸收电流可达50mA。必须注意软件上必须同时对这两个引脚写入相同的值同开同关。每个并联的引脚必须串联自己独立的限流电阻如图16所示。这是为了防止在开关瞬间由于两个内部MOS管导通速度的微小差异导致电流全部涌入先导通的那个管子而将其烧毁。电阻可以均衡电流。计算并联驱动LED 假设需要驱动一个功率LED需要80mA电流。我们可以并联4个IO口P0-P3。 每个IO口设计承担20mA。 限流电阻 R (VDD - Vf - VOL) / (If_total / 4) (5 - 3.2 - 0.5) / 0.02 65Ω。 可以选择68Ω电阻并确保软件中同时控制P0-P3这四个位。4. 进阶技巧与深度避坑指南4.1 电源与复位管理宽电压范围PCA8574工作电压为2.3V至5.5V且IO口可耐受5.5V电压。这意味着它可以很方便地在3.3V和5V系统间做电平转换。例如一个3.3V的MCU可以通过它控制5V的外设。上电复位POR芯片内部有POR电路当VDD电压低于约1.8V-2.0V时芯片复位所有端口恢复为输入模式弱上拉高电平。如果你的系统有复杂的上电时序需要注意POR可能带来的初始状态问题。例如在MCU的IO口还未配置好时PCA8574的端口可能已经输出导致意外动作。一个稳妥的做法是在MCU初始化完成后再通过I2C对PCA8574进行明确的配置。低功耗设计静态电流典型值仅4.5µA非常适合电池供电设备。在深度睡眠时确保I2C总线处于空闲状态SCL和SDA被上拉为高MCU的中断引脚配置正确即可利用其中断功能唤醒整个系统。4.2 通信可靠性提升总线电容与上拉电阻I2C总线有最大容限通常400pF。如果总线过长、设备过多会导致上升沿变缓通信失败。计算公式Rp(max) (tr / 0.8473 * Cb)其中tr是上升时间要求Cb是总线总电容。在400kHz下要求更严格。如果通信不稳定可以尝试减小上拉电阻如从10kΩ降到2.2kΩ但会增加功耗。软件超时与重试任何I2C通信函数都必须实现超时和错误重试机制。特别是对于中断方式在读取端口状态时如果I2C通信失败会导致中断无法清除INT线持续为低系统死锁。中断防抖对于机械按键等输入信号可能在接触时产生抖动导致短时间内多次触发中断。可以在MCU的中断服务程序中加入简单的延时去抖如10-20ms或者更优的做法是在中断中只设置标志位在主循环中延时后读取端口状态并进行处理。4.3 与类似芯片的选型对比除了PCA8574NXP现恩智浦家族还有其它选择了解它们有助于正确选型型号I2C速度电压范围地址数中断复位总灌电流特点与适用场景PCF8574/A100 kHz2.5V-6V8是否80 mA老型号速度慢驱动能力弱用于兼容旧设计。PCA8574/A400 kHz2.3V-5.5V8是否200 mA本文主角性能均衡最常用。PCA9674/A1 MHz (Fm)2.3V-5.5V64是否200 mA高速版地址更多适合大型复杂系统。PCA96701 MHz (Fm)2.3V-5.5V64否是200 mA用硬件复位替换了中断需要复位功能时选用。PCA96721 MHz (Fm)2.3V-5.5V16是是200 mA兼具中断和复位但地址减少。选型建议新设计首选PCA8574/74A或PCA9674/74A。前者性价比高后者速度和地址空间更优。如果需要通过硬件引脚复位芯片比如在系统死锁时考虑PCA9672或PCA9670。务必确认封装是否合适TSSOP和SSOP封装更省空间。4.4 调试与故障排查实录在实际项目中你可能会遇到以下问题问题1I2C通信完全无应答地址扫描不到。排查硬件连接用万用表检查VDD、GND、SCL、SDA、A0/A1/A2地址线是否连接正确有无虚焊、短路。确认SCL和SDA的上拉电阻已焊接。地址确认确认A2/A1/A0的电平设置并计算出正确的7位地址。用逻辑分析仪或示波器抓取I2C总线波形看主控发出的地址是否与芯片设置匹配。电源与复位测量芯片VDD电压是否在范围内。尝试给系统一个完整的上电复位。问题2可以读写但输出控制LED不亮或亮度异常。排查准双向口特性确认你是否在点亮LED时向对应位写的是‘0’输出低。写‘1’是无法点亮以共阳方式连接的LED的。驱动能力测量LED两端电压。如果引脚电压在输出‘0’时远高于0.5V例如达到1V以上说明电流过大芯片内部压降增加。检查你的限流电阻是否太小计算电流是否超过25mA单口或200mA总限。外部冲突检查是否有其他电路在试图驱动同一个引脚。准双向口输出高时很弱极易被外部信号拉低。问题3中断功能不正常一直拉低或从不触发。排查INT上拉确认INT引脚已通过电阻上拉到VDD。中断清除确保你的MCU在中断服务程序中执行了有效的I2C读操作。写操作也能清除中断但如果你只是想读取状态就用读操作。初始状态上电后如果某个输入引脚被外部拉低如按键按下INT会立即有效。你的初始化代码应该在配置完MCU中断后立即读取一次PCA8574端口状态以清除这个上电中断。信号毛刺用示波器观察INT引脚和对应的输入引脚波形。是否有抖动中断服务程序处理速度是否跟得上快速变化的信号可能需要硬件RC滤波或软件去抖。问题4多个设备中断线“线与”后无法区分中断源。解决方案这是“线与”逻辑的固有特点。MCU中断触发后必须进行“轮询查询”。编写一个函数按优先级顺序依次读取总线上每个PCA8574的端口状态并与上一次记录的状态比较找出发生变化的设备。可以将这个查询过程放在中断服务程序或一个低优先级任务中。经过上面这些从原理到实战再到深度优化的梳理相信你已经对PCA8574/74A这颗经典的I2C I/O扩展芯片有了透彻的理解。它的设计哲学是在简单与高效之间取得平衡虽然准双向口和中断逻辑有一些需要特别注意的“脾气”但一旦掌握它就成为了嵌入式工程师手中扩展IO、优化系统架构的利器。在实际项目中我习惯在原理图库中就把它的地址引脚用电阻做好上拉/下拉在PCB布局时将它靠近需要驱动的负载在代码中封装好初始化和读写函数并处理好中断的防抖与清除。这样当项目需要增加几个指示灯、多接几个传感器时就能从容不迫地拿出这颗“法宝”快速实现功能。