1. 项目概述与核心价值
在嵌入式开发中,我们常常会遇到一个经典难题:主控芯片的GPIO(通用输入输出)引脚不够用了。无论是驱动一片复杂的LED点阵屏,还是连接一堆传感器、按钮和继电器,有限的引脚资源总是捉襟见肘。这时候,I2C总线的I/O扩展器就成了我们的“救星”。它就像给你的微控制器(MCU)增加了一个外挂的、可远程控制的“手脚”,通过简单的两根线(SDA和SCL),就能轻松管理额外的16个、32个甚至更多的数字IO口。
今天要深入聊的,是NXP公司推出的一款经典升级产品——PCA9673。如果你用过它的前身PCF8575,那么PCA9673对你来说就是一次“加量不加价”的体验升级。它不仅仅是PCF8575的引脚兼容替代品,更在总线速度、驱动能力和系统可扩展性上带来了质的飞跃。简单来说,它把I2C总线速度从400 kHz提升到了1 MHz(Fast-mode Plus),单个引脚的灌电流能力保持在25 mA,但整颗芯片的总灌电流能力从100 mA提升到了400 mA,这意味着你可以同时点亮更多的LED而不用担心芯片过热。此外,地址选择从8个翻倍到16个,让你能在同一条I2C总线上挂载更多的扩展器,构建更庞大的IO矩阵。
我之所以花时间深入研究这颗芯片,是因为在最近一个工业控制面板的项目中,需要驱动近百个状态指示灯并读取数十个按键。最初方案用了多片PCF8575,但在调试高刷新率的LED PWM调光时,400 kHz的总线速率成了瓶颈,通信延迟明显。换成PCA9673后,通信流畅度大幅提升,系统响应更加实时。接下来,我将结合数据手册和实际踩坑经验,为你拆解PCA9673的方方面面,从原理到实战,从电路设计到代码驱动,让你不仅能看懂,更能用好这颗高性能的IO扩展“利器”。
2. PCA9673核心特性与架构深度解析
2.1 关键特性对比与选型考量
选择PCA9673,而不是其他I/O扩展器(如MCP23017、PCF8574A等),主要基于以下几个核心优势,这些优势直接决定了它在特定场景下的不可替代性:
高速I2C总线(Fast-mode Plus, 1 MHz):这是相对于PCF8575(Fast-mode, 400 kHz)最显著的提升。1 MHz的时钟频率意味着数据吞吐率翻倍还多。在需要快速扫描大量IO状态(如矩阵键盘)或进行LED PWM调光时,更高的总线速度能显著降低通信时间,提高系统整体响应速度。例如,连续读写16位数据,在1 MHz下比400 kHz节省一半以上的时间。
强大的总线驱动与灌电流能力:
- SDA线30 mA灌电流:这个参数对于总线稳定性至关重要。I2C总线依靠上拉电阻拉到高电平,当多个设备(尤其是容性负载大的长总线)同时下拉SDA线时,需要较强的下拉能力才能产生干净、陡峭的下降沿。PCA9673的30 mA驱动能力,使得它能够直接驱动高达4000 pF的总线电容,这意味着你可以在一条总线上挂载更多的设备,而无需额外添加总线缓冲器(如PCA9515)。
- 每引脚25 mA,整片400 mA灌电流:单个引脚25 mA的驱动能力足以直接驱动一颗标准LED(通常工作电流5-20mA)。整片400 mA的总能力,意味着16个引脚可以同时以25 mA驱动而芯片不会过载。这对于需要同时点亮多颗高亮LED的显示应用是硬性保障。相比之下,PCF8575的100 mA总能力就显得有些局促。
灵活的寻址与中断机制:
- 16个可编程从机地址:通过两个地址引脚(AD0, AD1)的四种电平组合(接VSS, VDD, SCL, SDA),可以产生16个不同的7位I2C地址。这极大地扩展了单条总线上可挂载的器件数量,为大型IO扩展系统提供了可能。
- 开漏中断输出(INT):这是一个非常实用的功能。当任何配置为输入的端口状态发生变化(上升沿或下降沿)时,INT引脚会被拉低,通知主MCU。这样MCU无需持续轮询(Polling)IO状态,可以进入低功耗休眠模式,等中断唤醒后再去读取数据,既省电又提高了响应效率。
宽电压范围与高可靠性:工作电压2.3V至5.5V,IO口可耐受5.5V电压,兼容3.3V和5V系统。具备内部上电复位(POR)、硬件复位引脚(RESET)和软件复位命令,确保系统能从异常状态可靠恢复。ESD保护、闩锁测试等指标也符合工业级应用要求。
2.2 内部架构与工作原理解析
理解PCA9673的内部框图(对应手册中的Fig 1),是正确使用它的基础。我们可以把它想象成一个带“秘书”的16位数据收发站。
核心组成部分:
- I2C总线控制与移位寄存器:这是芯片的“通信与翻译中心”。它负责解析主设备发来的I2C协议帧(包括地址、读写命令和数据),将串行数据转换为并行数据输出到端口,或者将端口并行数据收集起来,串行发送给主设备。内部的移位寄存器是数据暂存的地方。
- 16位准双向IO端口:这是芯片的“手脚”。每个端口内部结构如手册Fig 2所示。所谓“准双向”,是这类IO扩展器的一个关键特点。它内部有一个弱上拉电流源(
IOH,约30µA)和一个在写高电平时会短暂开启的强上拉晶体管(Itrt(pu),约1mA)。当端口被写入“1”时,它表现为一个带上拉的输出,可以输出高电平;当端口被写入“1”但外部被拉低时,它可以作为输入,读取低电平。特别注意:在用作输入前,必须先通过写操作将该端口设为“1”(高电平)。上电后,所有端口默认就是高电平,因此可以直接用作输入。 - 中断逻辑:这是芯片的“警报器”。它持续监控所有16个端口的状态。任何一个端口(无论当前是输入还是输出模式)的电平发生跳变,中断逻辑都会检测到,并将开漏输出的INT引脚拉低,向主MCU发出警报。
- 复位逻辑:包含上电复位(POR)、硬件复位(RESET引脚)和软件复位。确保芯片从一个已知的、稳定的状态开始工作。
工作流程简述:
- 写操作:MCU发送写命令和两个字节数据。第一个字节对应P07-P00,第二个字节对应P17-P10。数据经过移位寄存器,在应答(ACK)后锁存到输出端口。
- 读操作:MCU发送读命令。芯片将当前16个端口的状态(对于输出口,是上次写入的值;对于输入口,是外部实际电平)通过移位寄存器串行发出。一个关键细节:读取操作本身会清除该字节对应端口(第一个字节清P0x,第二个字节清P1x)产生的中断标志。
- 中断响应:外部输入变化→中断逻辑置位→INT引脚变低→MCU中断服务程序被触发→MCU发起读操作→读取数据并清除中断。
3. 硬件电路设计与实战要点
3.1 最小系统电路搭建
要让PCA9673跑起来,一个最简化的电路连接是必须的。下图展示了一个典型的最小系统连接,我们以此为基础进行讲解。
VDD (2.3V-5.5V) | +---[10kΩ]---+--- VDD (Pin 24) | | MCU PCA9673 GPIO/INT ---<--- INT (Pin 1) GPIO ---<--- RESET (Pin 3) *可选,接VDD则禁用 SDA <----> SDA (Pin 23) SCL ------> SCL (Pin 22) | | +---[10kΩ]---+--- SDA/SCL (上拉) | | GND VSS (Pin 12) | GND关键元件与连接解析:
电源与地(VDD, VSS):
- 电源范围2.3V-5.5V,需确保稳定。建议在芯片VDD引脚附近放置一个0.1µF的陶瓷去耦电容,以滤除高频噪声。
- VSS必须可靠接地。对于HVQFN和DHVQFN封装,底部的散热焊盘(Exposed Pad)也必须接地,以增强散热和电气性能。PCB设计时,应在该焊盘下方打过孔阵列连接到地层。
I2C总线(SDA, SCL):
- 上拉电阻:这是I2C总线正确工作的灵魂。SDA和SCL线都是开漏输出,必须通过上拉电阻拉到VDD。电阻值的选择是一个权衡:
- 阻值太大:上升沿变缓,可能无法在高速(1 MHz)下满足上升时间要求,导致通信失败。
- 阻值太小:当总线被拉低时,电流过大,增加功耗,并可能超过引脚的最大下拉电流。
- 计算与选型:通常,在标准模式(100kHz)和快速模式(400kHz)下,4.7kΩ(5V系统)或2.2kΩ(3.3V系统)是常见选择。对于PCA9673支持的Fast-mode Plus(1 MHz),总线电容必须更小,上升时间要求更严格。经验公式:上升时间
t_r = 0.3 * R_pullup * C_bus。假设总线总电容C_bus为200pF,要求t_r < 120ns(1MHz时钟周期的一半的1/3左右),则可计算出R_pullup < 120ns / (0.3 * 200pF) ≈ 2kΩ。因此,在1MHz应用中,建议使用1kΩ到2.2kΩ的强上拉电阻。如果总线上设备多、走线长,电容大,可能需要减小电阻值或使用专用的I2C总线缓冲器。
- 上拉电阻:这是I2C总线正确工作的灵魂。SDA和SCL线都是开漏输出,必须通过上拉电阻拉到VDD。电阻值的选择是一个权衡:
地址引脚(AD0, AD1):
- 这两个引脚决定了芯片的7位I2C从机地址。它们可以连接到VSS(GND)、VDD、SCL或SDA。特别注意:芯片内部没有上拉电阻,因此必须外部连接到确定的电平,不能悬空!悬空会导致地址不确定,通信失败。
- 地址映射表(手册Table 3)是配置的钥匙。例如,将AD1接GND,AD0接GND,得到的地址是0x40(7位地址,读写位另算)。在代码中,我们通常使用8位地址(包含读写位),写地址是
0x40 << 1 = 0x80,读地址是0x81。
中断引脚(INT):
- 开漏输出,必须接上拉电阻(通常4.7kΩ-10kΩ)到VDD。当任何端口状态变化时,此引脚被内部晶体管拉低。可以连接到MCU的具有外部中断功能的GPIO上,配置为下降沿或低电平触发。
复位引脚(RESET):
- 低电平有效。可以连接到MCU的一个GPIO,用于在程序跑飞或需要强制初始化时硬件复位芯片。如果不需要此功能,直接接VDD(通过一个10kΩ电阻上拉更安全)即可。
3.2 高电流负载与多片级联设计
驱动LED等大电流负载:PCA9673每个引脚最大可吸入25mA电流。驱动一个LED的典型接法是:LED阳极接VCC(可通过一个限流电阻),阴极接PCA9673的IO引脚。当IO输出低电平时,LED点亮。
- 限流电阻计算:
R = (VCC - Vf_led) / I_led。例如,VCC=5V,红色LED压降Vf≈2.0V,期望电流I_led=10mA,则R = (5-2)/0.01 = 300Ω。选择330Ω标准电阻即可。 - 并联增强驱动:如果需要驱动电流超过25mA的负载(如继电器线圈、大功率LED),可以将同一端口组(P0或P1)内的两个甚至多个引脚并联使用。例如,将P00和P01短接后共同驱动一个负载,理论最大电流可达50mA。务必注意:软件上必须将并联的引脚同时设置为输出,并输出相同的电平(同为高或低),否则会导致电流在芯片内部倒灌,损坏器件。
多片级联与中断处理:当一条I2C总线上挂载多个PCA9673时,中断线的处理需要技巧。如手册Fig 17所示,所有芯片的INT引脚可以通过一个“线与”逻辑连接到MCU的同一个中断引脚上(即所有INT引脚直接连在一起,并通过一个公共上拉电阻上拉)。
- 工作原理:只要有任何一片PCA9673的端口状态发生变化,其INT输出低电平,就会将公共的中断线拉低,触发MCU中断。
- 中断源识别:MCU进入中断服务程序后,并不知道是哪片芯片触发了中断。此时,MCU需要轮询总线上所有的PCA9673,依次尝试读取它们的端口数据。在读取某一片的数据时,如果这片芯片有未处理的状态变化,其INT引脚会在读操作完成后被内部释放(变高)。通过这种方式,MCU可以找出所有产生中断的芯片并进行处理。这种“线与”接法节省了MCU的IO口,但中断服务程序需要包含扫描逻辑。
4. 软件驱动与通信协议实战
理解了硬件,我们来看软件如何“驾驭”这颗芯片。这里以常见的STM32 MCU和HAL库为例,讲解关键操作。
4.1 设备初始化与基础读写
首先,需要根据硬件连接定义设备地址和I2C句柄。
// 假设 AD1 = GND, AD0 = GND, 查表得7位地址为 0x40 #define PCA9673_I2C_ADDR_WRITE ((0x40 << 1) | 0x00) // 0x80 #define PCA9673_I2C_ADDR_READ ((0x40 << 1) | 0x01) // 0x81 extern I2C_HandleTypeDef hi2c1; // 你的I2C外设句柄 // 初始化函数:通常无需特殊配置,但可以发一个复位序列或读取一次端口以确保通信正常 uint8_t PCA9673_Init(void) { uint8_t data[2] = {0xFF, 0xFF}; // 准备写入全1的数据 // 尝试写入全1,将所有端口初始化为输入模式(准双向口写1即输入态) if(HAL_I2C_Master_Transmit(&hi2c1, PCA9673_I2C_ADDR_WRITE, data, 2, 100) != HAL_OK) { return 0; // 通信失败 } return 1; // 初始化成功 }写入16位端口数据:写入操作必须一次写入两个字节(16位)。第一个字节控制P07-P00,第二个字节控制P17-P10。比特顺序是低位在前(LSB first)。
// 设置P07-P00 = 0xAA (1010 1010), P17-P10 = 0x55 (0101 0101) uint8_t PCA9673_WritePort(uint16_t data) { uint8_t buf[2]; buf[0] = (uint8_t)(data & 0xFF); // 低字节 -> Port 0 buf[1] = (uint8_t)((data >> 8) & 0xFF); // 高字节 -> Port 1 return (HAL_I2C_Master_Transmit(&hi2c1, PCA9673_I2C_ADDR_WRITE, buf, 2, 100) == HAL_OK); }读取16位端口状态:读取操作会返回当前所有16个引脚的电平状态。同样,第一个字节是P07-P00,第二个字节是P17-P10。
// 读取当前16个IO口的状态 uint16_t PCA9673_ReadPort(void) { uint8_t buf[2] = {0, 0}; uint16_t result = 0; if(HAL_I2C_Master_Receive(&hi2c1, PCA9673_I2C_ADDR_READ, buf, 2, 100) == HAL_OK) { result = (uint16_t)buf[1] << 8; result |= (uint16_t)buf[0]; } return result; // 如果失败,返回0,实际应用中应增加错误处理 }4.2 中断模式下的高效编程
利用INT引脚实现中断驱动,是发挥PCA9673效能的关键。以下是配置步骤:
- 硬件连接:将PCA9673的INT引脚连接到MCU的一个支持外部中断的GPIO,例如PA0。
- MCU端GPIO与中断配置(以STM32CubeMX/ HAL为例):
- 将该GPIO配置为外部中断模式,触发方式为下降沿触发(因为INT是低电平有效,状态变化时产生下降沿)。
- 在NVIC中使能对应的外部中断通道。
- 软件流程:
- 主程序初始化:调用
PCA9673_Init(),将所有期望作为输入的端口写1。 - 中断服务程序(ISR):
// 假设中断线连接到EXTI0 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == GPIO_PIN_0) { uint16_t port_status = PCA9673_ReadPort(); // 处理端口状态变化... // 例如,检查哪个按键被按下,哪个传感器信号变了 process_io_change(port_status); } } - 关键点:
PCA9673_ReadPort()函数被调用时,其内部操作会自动清除对应端口的中断标志。读取第一个字节清除P0x相关中断,读取第二个字节清除P1x相关中断。因此,在ISR中必须完成一次完整的16位读取,才能确保中断被清除,否则INT线会一直保持低电平。
- 主程序初始化:调用
4.3 软件复位与设备ID读取
这两个是PCA9673提供的高级功能,用于系统管理和诊断。
软件复位:通过向I2C总线的“通用呼叫地址”(0x00)发送特定序列(0x06),可以复位总线上所有支持此功能的PCA9673芯片。这在系统需要整体复位而又不想操作硬件复位引脚时非常有用。
void PCA9673_SoftwareReset(void) { uint8_t general_call_addr = 0x00; // 通用呼叫地址,写模式 uint8_t reset_cmd = 0x06; // 发送序列:START + 0x00 + ACK + 0x06 + ACK + STOP HAL_I2C_Master_Transmit(&hi2c1, general_call_addr, &reset_cmd, 1, 100); // 复位后芯片需要一点时间恢复,可加短暂延时 HAL_Delay(1); }读取设备ID:每个PCA9673都有一个唯一的设备ID,包含制造商代码、部件号和版本号。这可用于在总线上自动识别和检测器件类型,提高代码的通用性。
typedef struct { uint8_t manufacturer; // 制造商ID,NXP为0x00 uint16_t part_id; // 部件ID,PCA9673为特定值 uint8_t revision; // 硅片版本 } PCA9673_DeviceID_t; uint8_t PCA9673_ReadDeviceID(PCA9673_DeviceID_t *id) { uint8_t dev_id_addr_write = 0xF8; // 设备ID地址,写模式 (1111 1000) uint8_t dev_id_addr_read = 0xF9; // 设备ID地址,读模式 (1111 1001) uint8_t buf[4] = {0}; uint8_t slave_addr = PCA9673_I2C_ADDR_WRITE; // 目标芯片的写地址 // 步骤1: START + 0xF8 (W) + ACK // 步骤2: 发送目标芯片的7位从机地址(A6-A0),最后一位为Don't care(通常写0) // 步骤3: Repeated START + 0xF9 (R) + ACK // 步骤4: 连续读取3个字节 // HAL库没有直接支持此复杂序列的函数,需使用Mem_Read或组合调用 // 这里简化表示流程,实际使用HAL_I2C_Mem_Read可能更简单,但需注意起始条件 // 更可靠的方法是使用底层LL库或直接寄存器操作控制I2C时序 // 伪代码流程: // 1. 生成START // 2. 发送0xF8,等待ACK // 3. 发送(slave_addr & 0xFE),等待ACK (目标芯片应答) // 4. 生成Repeated START // 5. 发送0xF9,等待ACK // 6. 读取3个字节,对前两个字节回ACK,对最后一个字节回NACK // 7. 生成STOP // 由于HAL库封装,实现略复杂。一个替代方案是使用支持此协议的专用库或手动控制I2C时序。 // 成功读取后,解析buf: // id->manufacturer = buf[0]; // id->part_id = ((buf[1] & 0x1F) << 8) | buf[2]; // 根据手册Fig 11解析 // id->revision = buf[3] & 0x07; return 0; // 返回成功/失败 }注意:读取设备ID的I2C序列比较特殊,使用了重复起始条件(Repeated START)和保留地址。许多MCU的硬件I2C外设和标准HAL库函数可能无法直接生成此序列,可能需要更底层的操作或使用支持“通用调用”和“设备ID”模式的驱动库。
5. 常见问题排查与实战经验分享
在实际项目中使用PCA9673,难免会遇到各种问题。下面是我总结的一些典型故障现象、排查思路和解决方案。
5.1 通信失败类问题
问题1:I2C通信无应答(NACK),用逻辑分析仪或示波器抓不到波形。
- 排查步骤:
- 检查电源和地:用万用表测量PCA9673的VDD和VSS之间电压,是否在2.3V-5.5V范围内且稳定。
- 检查I2C上拉电阻:确认SDA和SCL线上有上拉电阻(通常4.7kΩ),且电阻值在高速模式下足够小(如1-2.2kΩ)。常见坑:忘记焊接上拉电阻,或电阻值过大导致上升沿太慢。
- 检查地址引脚:确认AD0和AD1已连接到确定的电平(VDD, VSS, SCL, SDA),绝对不能悬空。悬空时电平不确定,导致地址错误。
- 检查硬件连接:确认SDA、SCL线与MCU连接正确,没有虚焊、短路。SCL和SDA线不要接反。
- 检查MCU I2C配置:确认MCU的I2C外设已正确初始化(时钟、引脚复用),模式为主机模式,时钟速度不超过PCA9673支持的1 MHz。
- 使用地址扫描:写一个简单的I2C地址扫描程序,遍历所有可能的地址(0x08 - 0x77),看是否能收到应答,以确认芯片地址和基本通信是否正常。
问题2:通信时好时坏,偶尔出现数据错误。
- 排查步骤:
- 观察波形:使用示波器或逻辑分析仪捕获SDA和SCL波形。重点看:
- 上升时间:是否过于缓慢(圆角),尤其是在1 MHz速率下。如果缓慢,减小上拉电阻值或检查总线电容是否过大(线太长、负载太多)。
- 毛刺与振铃:是否存在过冲或振铃,这可能由阻抗不匹配或信号反射引起。可以在SCL和SDA线上串联一个小的阻尼电阻(如22Ω-100Ω),靠近MCU输出端放置。
- 检查电源噪声:在PCA9673的VDD引脚处用示波器交流耦合观察,是否有大的噪声或纹波。加强电源滤波,增加一个10µF的钽电容并联0.1µF陶瓷电容。
- 检查中断线干扰:如果使用了INT功能,确保INT信号线远离噪声源(如开关电源、电机驱动线)。可以在INT线上增加一个100pF的对地电容滤除高频噪声。
- 软件时序:在连续快速读写操作之间加入微小延时(几微秒),确保芯片有足够时间处理内部状态。
- 观察波形:使用示波器或逻辑分析仪捕获SDA和SCL波形。重点看:
5.2 功能异常类问题
问题3:配置为输出的引脚,输出高电平时电压不够高,带载能力弱。
- 原因与解决:PCA9673的准双向口在输出高电平时,主要依靠内部弱上拉电流源(约30µA)。这个电流很小,如果外部负载(如LED的限流电阻太小,或输入到其他高阻抗CMOS器件)有较大的输入漏电流,就可能把高电平拉低。
- 解决方案:当需要输出稳定的高电平时,应将该引脚用作输入(即向该位写1)。此时引脚处于高阻态,由外部电路(如上拉电阻)来决定电平。如果需要驱动电流,应使用低电平输出(灌电流)方式,这是它的强项。
问题4:中断功能不触发,或触发后无法清除。
- 排查步骤:
- INT引脚配置:确认INT引脚已通过上拉电阻连接到VDD,并且MCU端配置为浮空输入或上拉输入,中断触发方式为下降沿或低电平。
- 输入端口初始状态:在将某个引脚用作输入前,必须先通过写操作向该位写入“1”。上电默认是1,但如果之前写过0,需要重新写1。
- 中断清除机制:牢记PCA9673的中断清除是“字节读取关联”的。读取第一个数据字节会清除P0端口(P07-P00)的中断,读取第二个数据字节会清除P1端口(P17-P10)的中断。常见错误:在中断服务程序中只读取了一个字节(比如只关心P0口的变化),导致P1口的中断标志未被清除,INT线持续为低。务必在ISR中完成一次完整的16位读取。
- 电平变化检测:中断是在端口电平发生变化时产生的。如果输入是一个稳定的低电平,不会持续产生中断。只有从高变低或从低变高时才会触发。
问题5:多片PCA9673并联驱动大电流负载时,芯片发热严重。
- 原因与解决:虽然单个引脚可承受25mA,整片可承受400mA,但这是在一定散热条件下的极限值。如果所有引脚长时间以最大电流工作,芯片结温会迅速升高。
- 解决方案:
- 计算功耗:
P = I_total * Vce_sat。假设16个引脚各输出20mA到地,Vce_sat约0.5V,则总功耗P = 16 * 0.02A * 0.5V = 0.16W。查看芯片的热阻参数(RθJA),估算温升。 - 加强散热:对于DIP或SO封装,确保PCB上有足够的铜皮帮助散热。对于QFN封装,务必按照手册要求,将底部散热焊盘良好地焊接在PCB的铺铜区域,并通过过孔连接到内部地平面。
- 降额使用:在高温环境下,应降低使用的电流值。例如,在85°C环境温度下,将单引脚电流限制在15mA以内。
- 使用外部驱动器:对于真正的大电流负载(如继电器、电机),应该用PCA9673驱动一个晶体管(如MOSFET)或专用驱动芯片(如ULN2003),由后者来承担大电流,PCA9673只提供控制信号。
- 计算功耗:
- 解决方案:
5.3 与PCF8575的兼容性与升级注意事项
PCA9673被设计为PCF8575的“Drop-in”替代品,但有一个细微差别必须注意,否则在中断应用中可能出问题。
差异点:中断清除时序。
- PCF8575:在读取完两个字节(完整的16位数据)后,中断输出(INT)才会被清除。
- PCA9673:中断清除是按字节进行的。读取第一个字节清除P0口中断,读取第二个字节清除P1口中断。
影响与对策: 如果你的原有代码是为PCF8575编写的,并且中断服务程序(ISR)中在读取一个字节后,根据数据内容可能提前退出(例如,只处理P0口的变化),那么这段代码在PCA9673上运行时,P1口的中断将永远无法被清除,导致系统“锁死”在中断状态。
- 升级检查:检查所有处理PCA9673/PCF8575中断的代码,确保无论何种情况,ISR都执行了一次完整的16位数据读取。
- 通用写法:在ISR中,先读取并保存完整的16位数据,然后再进行逻辑处理。这样无论对PCF8575还是PCA9673都是安全的。
最后,分享一个调试小技巧:在PCB布局时,尽量将I2C的上拉电阻靠近PCA9673放置,而不是靠近MCU。这有助于改善信号完整性,特别是在总线较长或负载较多的情况下。另外,如果条件允许,保留一个测试点,可以将INT引脚通过一个跳线帽断开,这样在调试复杂的中断问题时,可以快速判断是PCA9673的问题还是MCU中断配置的问题。