2026/9/9 4:44:29

I2C IO扩展器选型与实战:从PCF8574到AW9523解决GPIO不够用

I2C IO扩展器选型与实战:从PCF8574到AW9523解决GPIO不够用 1. 从一次“不够用”的窘境说起做嵌入式开发的朋友十有八九都撞到过这堵墙主控芯片引脚算来算去就是差那么几个要么砍功能要么换大封装主控要么硬着头皮飞线。我之前做过一个带多路按键、LED、传感器供电控制的采集节点主控就一个36脚的MCU功能排下来GPIO缺口将近10个板子尺寸又卡得死死的换主控不现实。那会儿第一反应是“要不要上CPLD”后来冷静算了一笔账CPLD成本、开发环境、烧录器全加在一起解决10个IO的问题性价比实在太低。后来把目光放在I2C IO扩展器上一套流程跑下来板子顺利量产成本只多了几块钱。这个经历让我对IO扩展选型有了不少切身体会今天就把这些经验掰开揉碎聊一聊。这篇文章适合谁看硬件工程师评估方案阶段、嵌入式软件工程师在驱动层踩坑、以及自己画板子玩单片机的创客朋友。我会把方案对比、选型参数、实际代码、常见坑全部整理出来尽量让不同基础的读者都能拿去直接用。2. 主控GPIO不够用先别急着加芯片2.1 四种扩展方案的底层逻辑听到“GPIO扩展”很多人第一反应就是加一颗IO扩展芯片。实际上GPIO不够用有更朴素的解法只是大家习惯往复杂了想。我把常见的思路排了一下每个方案背后的本质逻辑完全不同。第一种是逻辑器件扩展。原理特别简单用74HC595这种串行移位寄存器把数据一位一位送进去然后并行输出。控制3个引脚就能换来8个输出级联以后还能继续翻倍。反过来输入端可以用74HC165把并行的电平一个一个读出来。优点是非常便宜一颗595几毛钱缺点也很明显想读输入就要换芯片想同时读又要加逻辑输出状态在上电瞬间是乱的得额外处理。第二种是I2C IO扩展。走的是标准通信协议主控I2C总线上挂一颗从设备通过写寄存器来控制引脚。一颗PCF8574有8个IO地址通过硬件引脚可以拼出8个地址一条总线上最多挂64个IO8颗芯片。优点是双向输入输出、占用引脚少两个引脚SCL/SDA即可、上电状态可控、可以直接级联。缺点是有通信时序的要求软件上比直接拉引脚多几行代码。第三种是SPI IO扩展比如MCP23S17。本质和I2C一样但SPI速率高不少适合需要高频翻转的场景代价是占用的引脚多一个至少SCK、MOSI、MISO、CS四个而且SPI设备没有像I2C那样灵活的地址硬件配置。第四种是模拟开关/译码器用74HC138这种3-8译码器组合出多路选择。这个方法看着省引脚实际用起来非常难受因为输出不锁存、没有寄存器概念每一次切换都要重新控制选择引脚。适合做扫描场景不适合做需要保持状态的输出。我在实际项目里的取舍逻辑是这样的纯输出点LED指示、没有输入需求、对成本极其敏感的用595。按键扫描、输入输出混用、需要保存输出状态的直接上I2C扩展。SPI扩展芯片除非速率确实不够否则不推荐纯属浪费引脚。2.2 I2C扩展器相对其他方案的三点优势I2C IO扩展器能够成为大多数场景的默认选择不是营销吹出来的而是三个硬核优势拍板的。第一个优势是引脚效率极高。整条I2C总线就两根线SCL、SDA只要地址不冲突想挂几个扩展器都可以。对比SPI的4根线以及595的至少3根控制线再加级联逻辑I2C完全是数量级的节省。更妙的是I2C天生支持多设备共用总线——你如果已经有传感器在总线上扩展器就是顺路捎带的事不需要额外占用任何引脚。第二个优势是输入输出双向可编程。PCF8574这类芯片虽然简单但每个引脚既可以配置为输入也可以驱动为输出这是595做不到的。实际项目中特别常见的一种需求是“这两个引脚平时当输入用我需要的时候要能把它拉低”——比方说去控制一个使能脚又想去检测它外部的电平状态。I2C扩展器一条指令切换方向硬件逻辑上天然支持。第三个优势是上电状态有保证。595在MCU配置之前输出是随机的可能导致继电器误动作。而I2C扩展器比如MCP23017带硬件复位引脚和上电默认寄存器值或者PCF8574上电后输出为高在设计电路时把这些状态考虑进去系统上电的确定性就高了。这一点在很多工业控制类产品里是硬性要求。3. 选型前必须搞懂的几个核心参数3.1 地址配置决定一条总线能挂多少芯片I2C通信之所以只需要两根线靠的是每个设备有独一无二的地址。IO扩展器选型第一个要关注的就是地址位宽。PCF8574有A0/A1/A2三个硬件引脚每个引脚能接高电平或低电平所以有2的3次方等于8种组合基地址是0x20后面细讲实际占用0x20到0x27这8个地址。MCP23017一样是3个地址引脚。TCA9535也是这样。AW9523是4个地址引脚支持16种地址组合。重点提醒一句看芯片手册时要注意区分“地址引脚数量”和“可挂载芯片数量”可挂载数量理论上等于2的地址引脚次方但是还要扣除总线上其他设备的地址占用。比如总线上已经有一个地址为0x20的传感器那么PCF8574从0x20开始的8个地址就有一个被占了实际最多只能挂7颗。设计时先把总线地址图列出来再决定选几颗芯片避免做到一半发现地址不够用只能改板。3.2 输出结构与驱动能力不是所有引脚都能推挽输出这是一个非常多新手栽跟头的地方。PCF8574的输出结构是开漏引脚内部没有推挽驱动只能拉低或者释放。释放以后靠外部上拉电阻把电平拉高。这意味着两件事第一引脚输出高电平的驱动能力取决于上拉电阻阻值想驱动LED必须外部加上拉电阻并且计算电流第二开漏结构天然适合“线与”多个引脚可以直接并联做逻辑或这在某些特殊电路里反而是个优势。MCP23017则不同它的输出寄存器默认是推挽输出可以直接推高和拉低驱动能力也更强单引脚能到25mA具体看电压带LED完全不需要外部上拉。TCA9535又回到了开漏结构。AW9523是这几颗里比较特殊的存在它支持推挽输出模式每个引脚可以通过寄存器配置为推挽或开漏。默认是开漏但改一个bit就能变推挽对驱动LED、继电器这种负载非常友好。而且AW9523内置了LED呼吸灯控制硬件自动产生PWM波形软件写寄存器就能做呼吸效果不用MCU软件模拟。选择逻辑很简单要驱动LED灯板、数码管段位选推挽输出或者开漏外部上拉的方案要接I2C总线做电平匹配开漏反而更方便。3.3 中断能力轮询还是等中断直接影响系统功耗很多人选IO扩展器只关心IO数量和地址完全忽略中断引脚。等到实际做低功耗项目的时候才后悔设备睡眠状态下IO扩展器检测到按键动作主控还在睡按键事件丢失。PCF8574是靠读引脚电平来感知外部变化的想检测按键必须不停轮询或者用一个独立的IO口同时接按键和扩展器。MCP23017带了INTA/INTB两个中断输出引脚当配置为输入的引脚电平发生变化时中断引脚会拉低主控可以从睡眠中唤醒再通过I2C读具体是哪个引脚变化了。TCA9535也是类似设计。AW9523的中断设计更灵活每个引脚可以独立使能中断并且支持边沿触发和电平触发。实际做低功耗产品时把扩展器所有输入引脚的中断都打开主控平时进入睡眠有外部事件通过INT引脚唤醒再读寄存器知道哪个按键触发整机功耗能压得很低。我建议只要目标产品要考虑电池供电或者待机功耗就直接选带中断的型号。哪怕第一版用不到多这个功能也不多花钱后续升级固件就能用上。3.4 速率等级与总线上拉电阻稳定性隐患从这里出I2C协议有标准模式100kHz、快速模式400kHz、快速模式1MHz这些等级。多数IO扩展器支持到400kHz看起来随便够用但很多人忽略了一个重要细节总线上拉电阻的取值和总线上挂的设备数量直接相关。总线电容越大上拉电阻就要越小否则信号上升沿太慢通信会出现随机错误。几个设备的电容叠加起来总线可能就不达标了。我做过多设备挂载的项目总线上挂了2颗PCF8574、1颗BH1750光照传感器、1颗EEPROM上拉电阻用的是4.7k总线速率跑到400kHz就偶尔出错换到2.2k才稳定。后来查了总线电容计算公式总线上挂的设备越多上拉电阻确实需要相应减小。需要注意是上拉电阻太小了会增大静态电流IO扩展器的输出低电平驱动能力有限如果上拉电阻太小引脚拉低时电流太大同样会导致通信失败。工程实践建议挂载设备少2~3个、总线长度短10cm以内用4.7k没问题设备多、走线长的用2.2k试极端情况可以上1k此时注意确认芯片输出低电平时的灌电流能力在规格书范围内。3.5 特殊功能是不是有“隐藏加分项”除了基础的IO读写现在新出的IO扩展器开始卷内置功能。AW9523的LED呼吸灯就是典型代表芯片内部有PWM模块可以独立控制每个引脚的呼吸频率和占空比。对于做氛围灯、跑马灯、键盘背光的项目这个功能能把MCU从软件模拟PWM中解放出来一个寄存器指令就搞定了。还有一类加分项是I2C地址可编程范围和两种供电域支持。AW9523支持1.8V到5V供电逻辑电平也可以单独匹配这样两边电压不一样也能直接用。选型时多翻几页手册的特殊功能章节经常能发现一些小惊喜。4. 五款主流I2C IO扩展芯片横向对比数据手册的参数比较枯燥我整理了一张表把目前市面上最常遇到的几款芯片放在一起方便快速定位。芯片型号IO数量输出结构地址数中断最大IO速率典型驱动能力特色功能典型价格区间PCF85748开漏8无100k/400k准双向灌电流约20mA最经典资料最多0.5~1.5元MCP2301716推挽8有1.7MHz约100k/400k兼容单引脚25mA输入极性可配置内部上拉可选3~6元TCA953516推挽准双向8有400k单引脚约10mATI主流低静态功耗2~5元AW952316推挽/开漏可配16有400k单引脚约20mALED呼吸灯/PWM电压范围宽2~4元PCA955516推挽8有400k单引脚约10mA老牌经典替代型号多2~5元选型的时候我一般这么走只做简单输出和少量输入、预算敏感选PCF8574成本最低代码网上满天飞IO需求超过8个、且要中断推挽看MCP23017和PCA9555MCP23017的资料和库更多做LED灯效类的优先考虑AW9523呼吸灯功能省掉大量软件工作功耗敏感、电池供电优先考虑TCA9535静态电流更低。实际项目中我用的最多的是PCF8574和AW9523前者纯打杂后者是后来发现的“真香”芯片。5. 实操演示PCF8574从硬件连接到驱动代码5.1 硬件连接与地址计算以STM32F103的主控为例I2C1的PB6连SCLPB7连SDA接PCF8574。SCL和SDA各接一颗4.7k上拉电阻到3.3V这是I2C标准接法不管芯片供电是3.3还是5V都要有上拉。PCF8574的供电接3.3V还是5V取决于后级负载的电平要求如果只控制LED和读取按键3.3V完全够用。A0/A1/A2三个脚决定从机地址接地时全部为0。PCF8574的7位地址是0100 000写操作时最低位为0所以完整写地址是0x40读地址是0x41。很多人的代码里写X0x20那是7位地址左移一位后的写法两者等价但要注意驱动库到底用的是7位地址还是8位地址。STM32标准库的I2C从机地址参数要去掉最低位所以填0x20有些封装好的Arduino库内部会自动左移填0x20它会再移一次结果就变成0x40了导致通信失败。这一点是新手最容易迷糊的地方。地址配置示例A21、A10、A01时7位地址是0100 101即0x25读写地址分别是0x4A和0x4B。5.2 代码实现先搞懂寄存器再写读写函数PCF8574只有一个8位寄存器往里面写0x00就是所有引脚拉低输出低电平写0xFF就是所有引脚释放开漏输出高外部上拉生效。读它的时候引脚的当前电平会以8位数据的形式返回。先看写操作代码我用的是STM32标准库风格的伪代码方便各种主控的朋友理解核心逻辑// 向PCF8574写数据控制8个IO输出 void PCF8574_Write(uint8_t devAddr, uint8_t data) { I2C_Start(); I2C_SendByte(devAddr 1 | 0); // 7位地址左移一位最低位写方向0 I2C_WaitAck(); I2C_SendByte(data); // 写入8位IO状态 I2C_WaitAck(); I2C_Stop(); }读操作需要注意一个细节读数据之前必须先把寄存器写成0xFF把所有引脚释放成输入状态否则引脚如果之前被锁存为低电平读回来的永远是0。很多人在读按键的时候卡住就是这个原因。// 从PCF8574读数据读取输入引脚电平 uint8_t PCF8574_Read(uint8_t devAddr) { uint8_t data; PCF8574_Write(devAddr, 0xFF); // 关键先释放引脚才能正确读取外部电平 I2C_Start(); I2C_SendByte(devAddr 1 | 1); // 7位地址左移一位最低位写方向1读 I2C_WaitAck(); data I2C_ReadByte(); // 读取8位IO状态 I2C_SendNack(); // 读最后一字节回NACK I2C_Stop(); return data; }实际用的时候PCF8574的输入输出是准双向的写0x00引脚拉低输出写0xFF引脚释放变成输入。如果想某个引脚既当输入又当输出只能靠程序频繁切换这是开漏结构的局限设计时要想清楚哪些引脚固定输入、哪些固定输出。5.3 按键输入的消抖与中断替代方案PCF8574没有中断引脚按键检测只能轮询。实测中轮询频率设在10~20ms就够过高浪费CPU过低则按键手感迟钝。按键消抖用软件延时或定时器实现状态机处理按下和释放两个沿。我在实际项目中的做法是用一个1ms的定时器每次进入中断扫描一次PCF8574把扫描结果存入全局变量主循环里做状态机判断。这样子按键响应时间1~5ms可调比较平滑。如果按键数量多且需要睡眠唤醒还是建议换到MCP23017或者AW9523靠外部INT引脚唤醒主控效率和功耗完全不是一个级别。6. AW9523进阶玩法一颗芯片解决LED灯效和按键输入6.1 为什么我会在项目中转投AW9523最开始用PCF8574做了一批样机功能没问题但客户提了新需求按键要多几个LED要做成呼吸效果。用PCF8574做呼吸灯只能在MCU里用PWM模拟8个LED就要8路PWMMCU的硬件定时器压根不够用。翻资料的时候看到AW9523发现它专门为这类场景设计内部有LED驱动模块每个引脚都可以独立配置PWM呼吸效果。从项目角度算了一笔账PCF8574加8路PWM占用的MCU资源、代码复杂度、调试时间比起AW9523多出来的两块多成本完全不划算。这个案例也让我认识到选型不能只看单价要把整个BOM成本、开发工时、调试风险全都算进去。6.2 AW9523关键寄存器速览与配置流程AW9523的寄存器映射比PCF8574复杂但常用也就那么几个。重点提两个一个是0x00的GCRGlobal Control Registerbit0是SWRST软件复位另一个是0x11到0x12的LED模式寄存器把对应位置1后引脚进入LED模式此时输出由PWM寄存器控制。配置一个引脚为推挽输出并做呼吸灯效果的流程如下// 1. 软件复位 AW9523_Write(0x00, 0x01); // GCR写0x01触发软复位 delay_ms(5); // 2. 配置引脚方向P0_0为输出 AW9523_Write(0x02, 0xFE); // 方向寄存器1为输入0为输出 // 3. 使能P0_0的LED模式 AW9523_Write(0x11, 0x01); // LED模式寄存器bit0对应P0_0 // 4. 设置呼吸频率和占空比 AW9523_Write(0x20, 0x80); // 呼吸频率控制 AW9523_Write(0x21, 0xFF); // 最大电流配置完以后引脚的呼吸效果由硬件自动产生MCU只需要在需要改变亮度或频率时才写一次寄存器。我做项目时一共控制了12路呼吸灯MCU负载几乎没有增加。6.3 AW9523实际项目中的注意事项AW9523有几个坑必须提醒。第一是默认状态不是推挽。芯片上电后是开漏模式直接驱动LED会发现亮度不对劲或者干脆不亮一定要按上面的配置流程把LED模式打开。第二是中断脚要加外部上拉。INT引脚是开漏输出必须接上拉电阻到供电电压否则电平不确定。我第一次画板子就忽略了这一点中断偶尔触发偶尔不触发排查了半天。第三是地址引脚不能悬空。AW9523有4个地址引脚数据手册要求必须接高或接低悬空会导致地址不稳。而PCF8574的地址引脚可以悬空默认有内部下拉习惯画PCB的人容易想当然。7. Linux和软件I2C场景下的扩展器调试技巧7.1 在Linux用户态直接操作IO扩展器不少嵌入式Linux项目也面临GPIO不够用的问题比如RK3229这种主控芯片原生的GPIO被系统占用了一堆想多控制几个设备还得靠扩展器。Linux内核里已经有PCF857x、MCP23017等驱动设备树配置好之后扩展器引脚会直接映射成gpiochip应用层用libgpiod操作即可。调试阶段最实用的是i2c-tools工具包里的i2cdetect扫描总线上挂载的设备地址i2cdetect -y 1输出如下说明0x20地址上有PCF85740 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --遇到扫描不到设备的情况优先检查上拉电阻是否焊接以及阻值大小SCL和SDA是否接反地址引脚配置是否和代码一致目标设备供电是否正常。7.2 软件模拟I2C的时序要点很多低成本方案用GPIO模拟I2C尤其是BH1750这类对时序要求不高的传感器配合软件I2C跑得很顺。但用软件模拟I2C驱动PCF8574这类扩展器时时延参数特别关键。I2C协议中起始条件要求SCL高电平时SDA拉低然后SCL再拉低时钟高电平的保持时间、数据建立时间都有最小要求。如果用GPIO模拟且不加延时时钟频率可能冲到1MHz以上部分扩展器跟不上数据出错。实测下来软件模拟I2C的GPIO翻转之间建议加至少1us的延时总体时钟频率控制在200kHz以内稳定性和摸查成本都比较好。注意GPIO配置为开漏输出模式并且在外部加上拉电阻不要用推挽模式去模拟I2C——否则多个设备挂同一总线时电平冲突会损坏端口。7.3 快速排查I2C总线被拉死怎么办实际调试中最高频的故障是总线被拉死现象是SDA或者SCL始终为低电平i2cdetect直接报总线错误。原因通常是以下几种某个从设备地址冲突同时响应导致总线混乱设备供电异常从设备闩锁进入异常状态把SDA拉低通信过程中主控复位I2C时序停在半截从设备在等待后续时钟。解决办法排优先级来试先把所有从设备断开只留扩展器看总线是否恢复再检查每个设备的上拉电阻确保所有上拉都接到电源最后如果主控支持时钟恢复发9个SCL脉冲把所有从设备的状态机复位。软件上还有一个临时办法每包数据发送前先把SDA和SCL都设置为输出高手动产生几个脉冲把从设备状态机踢回IDLE状态。这在Linux和裸机代码里都适用。8. 选型决策树与常见问题速查表8.1 按项目需求快速定位芯片我把这几年总结的选型逻辑整理成一套决策判断链拿去套用基本不会跑偏。先看功能需求需要输出大于8路且同时有输入需求选择IO扩展器。然后看是否有LED呼吸、PWM调光需求有则优先AW9523省MCU资源。如果没有特殊需求再看是否需要中断唤醒需要则选MCP23017或TCA9535不需要则PCF8574够用。最后看IO总数超过16个IO需求就评估多颗扩展器方案或者换更大引脚的主控。因为芯片本身单价差异不大MCP23017裸片价通常也就PCF8574的三四倍但开发工时、调试难度的差别远比芯片差价大。关键还是一句话优先把方案做简单。8.2 问题排查速查表实际项目中踩过的坑整理成表格方便现场排查时对照故障现象可能原因排查思路与解决方案i2cdetect扫描不到设备上拉电阻没焊接或阻值过大检查SCL/SDA对电源的4.7k~10k电阻示波器看SDA/SCL波形地址对了但读写无响应地址引脚接错或悬空用万用表量A0/A1/A2的电平确保和软件配置一致读按键返回值恒为0读之前没写0xFF释放引脚在读操作前先写0xFF把手动输出配置排除掉数据偶尔错一两个bit总线电容过大、上拉电阻太大换2.2k或1k上拉降低I2C速率到100kHz验证扩展器引脚输出高电平异常开漏结构没加上拉或上拉阻值大推挽输出芯片检查方向寄存器开漏芯片补外部上拉中断不触发中断脚上拉缺失或中断寄存器没使能检查INT引脚外部上拉逐个核对中断使能寄存器睡眠功耗偏高上拉电阻太小导致静态电流过大总线空闲时关闭I2C、引脚输出高优化整机功耗总线上多个设备互相干扰从机地址重叠列出总线地址分配表逐颗核对地址引脚组合8.3 关于电平匹配扩展器供电3.3V时IO输出高电平为3.3V。如果后级负载需要5V电平不能直接驱动TCA9535这类芯片虽然供电电压范围宽但IO高电平不会超过供电电压。做法是输出控制的负载如果兼容3.3V逻辑就直接接不兼容就加三极管或MOS管做电平转换。很多人在这里翻了车3.3V供电的PCF8574拉高LED是没问题的LED压降1.8V加上限流电阻3.3V减去后还有几毫安的亮度但用同样的接法驱动5V继电器就不行——继电器线圈需要5V驱动3.3V可能不够。控制这类负载建议改输出开漏外部上拉到5V的方式或者直接换5V供电的扩展器但要注意此时I2C总线上拉也要接到5V而主控是3.3V的话要把I2C做电平转换。9. 从扩展器到整个GPIO资源规划的思路项目做多了以后我发现扩展器只是手段真正的核心是资源规划。画原理图之前先把整板所有需要控制的信号列个清单按性质分类哪些是固定输出LED、继电器、使能脚哪些是固定输入按键、拨码开关、传感器状态哪些是双向且需要频繁切换数据/控制复用脚。分类之后先用主控的原生GPIO去匹配那些时序敏感的、需要中断的、需要模拟特殊协议的信号把类型固定、数量大的用扩展器解决这样主控GPIO的压力会小很多。再一个经验是别把扩展器放在I2C总线的末端如果总线上还有需要高速通信的传感器扩展器这种低速设备放在总线的中间或者靠近主控一端总线的电容分布会更均匀信号质量更好。我以前在画ESP32的开发板时就吃过亏把两颗PCF8574挂在总线最远端总线上还有一个IMU结果IMU的400kHz数据偶尔被扩展器拉歪后来把扩展器移到总线上部问题消失。10. 一个实际案例ESP32上的I2C IO扩展实战记录光说理论显得虚分享一个最近给客户做的实际项目。客户需要做一块控制板主控是ESP32IO要控制3路步进电机驱动器的方向/使能脚6个输出、4路按键输入、2路LED指示外加一个OLED显示屏。原生GPIO只有十几个上面需求一共占了12个还要留4个给下载和通讯彻底不够。方案OLED走硬件I2C总线的0x3C地址扩展器挂两颗PCF8574第一颗地址0x20管按键和LED第二颗地址0x21管电机驱动器控制脚。实际运作中有一个细节值得分享电机驱动器控制脚在系统初始化时必须拉到确定电平比如使能为低但PCF8574上电默认全高所以电路上我把使能脚设计成低电平有效PCF8574默认高电平正好让电机驱动器使能脚处于禁止状态避免了上电瞬间电机乱动。这就是前面说的“利用上电默认状态做安全设计”选型时就要想清楚。ESP32这边Arduino环境直接能用Adafruit的PCF8574库初始化后就是一个digitalRead/digitalWrite的透明接口。实际测试延时大概在几十微秒级别对低速控制完全够用。如果要用ESP32的硬件I2C记得查一下自己的板子用的Wire.begin的引脚编号。有些老代码里写Wire.begin(4, 5)但新版Arduino-ESP32的引脚分配可能不是这个接错了总线直接扫不到设备。11. 最后再聊几句体己话最开始做扩展器选型时我也被五花八门的型号搞晕过后来定了一个原则选最成熟的不选最新的。PCF8574技术老、资料全、生态大什么问题都能搜到答案。AWS逻辑里那些新功能是加分项但加分项只有在基础需求满足之后才有意义。还有一点是调试顺序。焊接完板子不要急着写驱动第零步永远是裸板量硬件通上电用万用表量扩展器VCC和GND确认SCL/SDA的电压都在高电平一般3.3V或5V然后示波器点一下扩展器SCL引脚和主控发出数据时是否有波形。这一步没问题了再跑软件扫描。希望大家做完这个项目不只是拿到一块能用的板子而是真正把I2C这套总线协议、扩展器选型逻辑和调试方法沉淀下来。以后不管是I2C扩展器还是别的I2C从设备只要总线通信有问题都能靠这套思路自己去定位而不至于到处搜帖子看代码。12. 扩展器常见参数对比表汇总把目前最常用的几款Io扩展器再汇总成一张表方便画板前对照查阅。参数PCF8574MCP23017TCA9535AW9523I2C地址范围0x20~0x270x20~0x270x20~0x270x40~0x4F16个组合输出模式开漏推挽推挽推挽/开漏可选中断引脚无2个2个1个输入内部上拉无有可配置无有可配置LED驱动无需外接限流电阻无但灌电流能力强无有内置PWM呼吸灯电压范围2.5V~5.5V1.8V~5.5V1.65V~5.5V1.8V~5V常用封装SOIC-16/TSSOP-16SOIC-28/SSOP-28SOIC-16/TSSOP-16QFN-24选型时先把这张表存下来对照自己的需求圈定1~2款再去看完整的数据手册。13. 关于驱动库与代码移植的补充很多朋友会问要不要用现成的驱动库。我的建议如果是快速原型验证用现成的库完全没问题省时间如果是做量产产品驱动代码建议自己写因为现有库通常做了太多兼容判断效率不高而且有些库的接口和硬件平台绑定移植到新主控反而处处受限。自己写I2C扩展器驱动其实很薄很薄。以PCF8574为例核心就两个函数写一个字节、读一个字节。底层I2C如果自己的工程里已经有稳定驱动那扩展器驱动就是上面代码里的几行而已。与其花时间去理解一个几百行的库不如看手册搞明白芯片内部寄存器自己动手写20分钟就搞定。另外提醒一下用HAL库的朋友STM32CubeMX生成的I2C代码默认是轮询模式配合扩展器读写没问题但禁止在中断回调函数里直接调用HAL_I2C_Master_TransmitI2C通信时序会被中断抢断导致错误。我自己遇到过几次HAL库标志位卡死的情况换为定时器轮询方式之后世界清净了。写到最后还是那句话工具是拿来用的不是拿来供的。I2C IO扩展器很简单但用好的前提是对芯片特性、总线机制和你的应用场景都心里有数。希望大家看完这篇文章能少踩几个我当年踩过的坑做板子顺顺利利。