1. I2C总线协议嵌入式世界的“轻声细语”在嵌入式系统的世界里芯片之间的对话方式多种多样有的像广播如CAN总线有的像打电话如SPI而I2CInter-Integrated Circuit则更像一场精心安排的“轻声细语”会议。它只用两根线——一根数据线SDA和一根时钟线SCL就能让挂在同一条总线上的多个设备有序地交换信息。这种简洁高效的设计让它成为了连接微控制器与各类传感器、存储器如EEPROM、实时时钟RTC乃至其他微控制器的首选协议之一。我接触过不少通信协议I2C的优雅之处在于它的“线与”逻辑和主从架构。所有设备的SDA和SCL引脚都通过上拉电阻接到正电源形成“开漏输出”。这意味着任何设备都可以通过将线拉低来输出‘0’而只有当所有设备都释放总线输出高阻态时上拉电阻才会将线拉高为‘1’。这种硬件设计天然支持多主设备仲裁——如果两个主设备同时开始传输谁先尝试发送‘1’而实际检测到‘0’谁就失去了总线控制权悄然退场。这种机制避免了数据冲突无需复杂的软件协调。对于像TI的TMS320F2837xD这类高性能双核实时微控制器来说内置的I2C模块不仅仅是协议控制器更是一个高度集成、可减轻CPU负担的智能外设。它支持标准模式最高100kbps和快速模式最高400kbps内置了16级深度的发送和接收FIFO还有专门的中断逻辑来处理各种通信事件。无论是作为主设备去轮询温度传感器还是作为从设备响应上位机的查询F2837xD的I2C模块都能应对自如。本文将带你从I2C的基础原理出发深入到F2837xD的I2C模块内部特别是那些手册里一笔带过、但在实际调试中至关重要的细节比如时钟配置的玄学、FIFO使用的坑以及如何通过SCIPRI等寄存器优雅地应对仿真器调试时的中断。无论你是刚开始接触嵌入式通信的新手还是想深入了解C2000系列MCU外设的老鸟相信都能找到有用的干货。2. I2C核心原理与F2837xD模块架构解析2.1 I2C协议的精髓时序、地址与数据帧要玩转I2C光知道两根线可不够得理解它那套独特的“语言规则”。所有的对话都始于一个START条件当SCL为高电平时SDA线上一个从高到低的跳变。这就像会议主持人敲了一下槌子说“大家注意我要开始讲话了。” 相应地STOP条件则是SCL为高时SDA从低到高的跳变意味着“我的话讲完了”。START之后主设备会发送一个7位或10位的从设备地址紧跟一位读写R/W位。‘0’表示主设备要写数据给从设备‘1’表示主设备要从从设备读数据。这里有个关键细节地址和数据的传输都是高位MSB在前。每个字节8位传输完毕后接收方必须在下个时钟脉冲期间拉低SDA线发出一个应答ACK信号如果接收方没有拉低保持高电平那就是非应答NACK通常意味着传输出错或接收方无法处理。F2837xD的I2C模块完全遵循这套规则并在硬件层面实现了状态机来自动处理这些复杂的时序。你只需要配置好寄存器它就能自动生成START、STOP、发送地址、检测ACK/NACK极大简化了软件负担。2.2 F2837xD I2C模块内部透视不止是状态机翻开F2837xD的技术参考手册I2C模块的框图看起来有点复杂但我们可以把它拆解成几个核心部分来理解时钟与同步系统这是I2C通信的“心跳”。模块时钟由系统时钟SYSCLK经过一个可编程预分频器I2CPSC产生。当模块作为主设备时这个模块时钟会进一步通过两个分频器I2CCLKL和I2CCLKH来产生最终输出到SCL引脚上的主时钟。手册里那个公式Fmod SYSCLK / (I2CPSC.IPSC 1)是计算的起点。关键在于I2C协议对时钟高低电平的时间有严格要求因此需要分别配置低电平周期ICCL和高电平周期ICCH。计算时还要加上一个由IPSC值决定的延迟dIPSC0时d71时d61时d5。最终的SCL频率公式为Fsc Fmod / [(ICCH d) (ICCL d)]。很多初学者直接套用公式算出的频率和实际示波器测出的对不上问题往往就出在忽略了这个d值。数据路径与FIFO数据发送时CPU或DMA将数据写入发送数据寄存器I2CDXR然后硬件自动将其转移到发送移位寄存器I2CXSR再一位一位地移到SDA线上。接收过程则相反。F2837xD的亮点在于其16x8位的发送和接收FIFO。启用FIFO后你可以一次性写入多个数据模块会在后台自动发送同时产生基于FIFO填充水平的中断极大减少了CPU中断频率提升了系统效率。但要注意FIFO的使能SCIFFTX. SCIFFEN和复位操作SCIFFTX. TXFIFO Reset需要在模块初始化时正确配置且最好在模块复位I2CMDR.IRS0状态下进行。中断与状态逻辑模块可以产生多种中断比如发送准备好、接收准备好、仲裁丢失、无应答NACK等。这些中断都映射到PIE外设中断扩展模块的特定通道。合理配置和使用这些中断是实现高效、可靠异步通信的关键。例如在从设备模式下当收到与本机地址匹配的地址帧时会触发“被寻址为从设备”中断此时软件需要迅速判断主设备是要读还是写并做好相应准备。仲裁与噪声滤波多主竞争时硬件仲裁逻辑会自动工作。此外SDA和SCL引脚上都集成了数字噪声滤波器可以有效滤除线上的毛刺提高在恶劣电气环境下的通信可靠性。这个功能通常默认开启但在极端高速或高噪声环境下可能需要根据实际情况调整。注意I2C总线是开漏输出必须外接上拉电阻。电阻值的选择是个权衡阻值太小电流大功耗高但上升沿陡峭适合高速阻值太大上升沿缓慢可能无法满足高速模式的时间要求。通常在3.3V系统、标准模式100kHz下4.7kΩ是一个常用值快速模式400kHz下可能需要减小到2.2kΩ甚至1kΩ。总线上所有设备的等效输入电容和布线电容也会影响上升时间最好根据TI的应用报告《I2C Bus Pull-Up Resistor Calculation》进行详细计算。2.3 核心寄存器组概览F2837xD的I2C模块有一组寄存器控制着其所有行为。除了数据寄存器I2CDXR I2CDRR以下几个配置寄存器尤为重要I2CMDR模式寄存器核心控制寄存器。包含模块使能位IRS、主从模式选择MST、重复模式RM、自由数据格式FDF、10位地址扩展XA、传输方向TRX等关键位。任何对MST、RM、FDF等模式的更改建议都在模块复位IRS0状态下进行配置完成后再使能模块IRS1以避免不可预知的行为。I2CPSC时钟预分频寄存器决定模块基础时钟频率。此寄存器仅在IRS0时可写。I2CCLKL 和 I2CCLKH时钟低/高时间寄存器共同决定作为主设备时SCL时钟的频率和占空比。I2COAR自身地址寄存器当模块作为从设备时此寄存器存放本设备的7位或10位I2C地址。I2CCNT数据计数寄存器在非重复模式RM0下此寄存器定义要传输的数据字节数。一个经典的坑是如果将其设置为0模块会认为要传65536个字节而不是0个字节。I2CSTR状态寄存器包含了总线忙BB、接收器就绪RRDY、发送器就绪XRDY、仲裁丢失AL、无应答NACK等关键状态位。软件需要通过查询或中断来检测这些状态以驱动通信流程。SCIFFTX 和 SCIFFRXFIFO控制寄存器用于使能FIFO、设置中断触发级别、复位FIFO等。3. 从零开始F2837xD I2C模块配置与驱动实现3.1 初始化配置引脚、时钟与基础模式在写第一行通信代码前扎实的初始化是成功的一半。以下是一个标准的主设备初始化流程我们结合代码和注释来讲解。// 假设使用I2C-A模块SCL引脚为GPIO32SDA引脚为GPIO33 void I2CA_Init(void) { // 步骤1: 配置GPIO引脚复用为I2C功能 // 先配置GPyGMUX如果需要高精度复用再配置GPyMUX。避免毛刺的关键是顺序。 // GPIO32 和 GPIO33 在F2837xD上通常属于不同的GPIO组需查数据手册确定具体寄存器。 // 例如假设它们在GPIO32-39组复用选择为I2C-A // GPIO_SetupPinMux(32, GPIO_MUX_CPU1, 2); // GPIO32复用为I2C-A SCL // GPIO_SetupPinMux(33, GPIO_MUX_CPU1, 2); // GPIO33复用为I2C-A SDA // 同时将GPIO配置为异步输入无数字滤波以匹配I2C协议要求。 // GPIO_SetupPinOptions(32, GPIO_INPUT, GPIO_ASYNC); // SCL // GPIO_SetupPinOptions(33, GPIO_INPUT, GPIO_ASYNC); // SDA // 步骤2: 使能I2C模块时钟如果系统有外设时钟使能控制 // SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_I2CA); // 步骤3: 软件复位I2C模块 (IRS 0) I2caRegs.I2CMDR.bit.IRS 0; // 步骤4: 配置I2C时钟关键步骤 // 假设系统时钟SYSCLK 200MHz目标I2C SCL频率为400kHz (快速模式) // a) 配置预分频器I2CPSC得到模块时钟Fmod (推荐7-12MHz) // 取I2CPSC 9则 Fmod 200MHz / (91) 20MHz (在7-12MHz范围外但手册是推荐值20MHz实测也可工作) // 更稳妥的选择取I2CPSC 24则 Fmod 200MHz / 25 8MHz (符合推荐) I2caRegs.I2CPSC.all 24; // IPSC 24 // b) 配置时钟低/高时间寄存器。根据公式Fsc Fmod / [(ICCHd) (ICCLd)] // Fmod 8MHz, Tmod 125ns。 // 目标 Fsc 400kHz, Tsc 2500ns。 // 通常设置ICCH ICCL以获得50%占空比。 // 设 ICCH ICCL N。 // 公式2500ns 125ns * [(Nd) (Nd)] 125ns * 2*(Nd) // 2*(Nd) 20 Nd 10。 // 由于IPSC241查表得 d5。 // 所以 N 10 - 5 5。 // 因此设置 I2CCLKL I2CCLKH 5。 I2caRegs.I2CCLKL 5; I2caRegs.I2CCLKH 5; // 步骤5: 配置自身地址仅当作为从设备时需要 // I2caRegs.I2COAR 0x48; // 例如设置7位从地址为0x24左移一位后为0x48 // 步骤6: 配置FIFO可选但强烈推荐用于高效传输 // 使能FIFO模式设置发送和接收FIFO的中断触发级别为8即FIFO半满/半空时触发 I2caRegs.I2CFFTX.all 0x6000; // 使能发送FIFO (I2CFFEN1) TXFFIL0 (触发级别0可改) I2caRegs.I2CFFRX.all 0x2020; // 使能接收FIFO RXFFIL2 (触发级别2) // 步骤7: 配置工作模式并启动模块 // 清除所有状态标志可选 I2caRegs.I2CSTR.all 0xFFFF; // 写1清标志 // 配置为主设备非重复模式标准7位地址非自由数据格式模块使能 I2caRegs.I2CMDR.all 0x0020; // IRS1, MST1, TRX0(初始为接收实际发送时会改), 其他位默认0 // 步骤8: 配置PIE中断如果需要使用中断 // 使能I2C-A的PIE中断向量注册中断服务函数等此处略 }实操心得时钟配置是I2C调试中最常见的“坑点”。务必使用示波器或逻辑分析仪实际测量SCL的频率和占空比与计算值对比。如果偏差较大检查d值是否用对以及SYSCLK频率是否准确。另外模块时钟Fmod必须在7-12MHz之间是手册的硬性要求是为了满足I2C协议严格的时序规范如建立时间、保持时间。超出此范围可能导致通信不稳定尤其在高低温环境下。3.2 主设备通信流程实现以读写EEPROM为例我们以读写一个常见的I2C EEPROM如AT24C02地址0x50为例展示完整的非重复模式主设备通信流程。这里采用查询方式便于理解。// 向EEPROM指定地址写入一个字节 uint16_t I2C_WriteByte(uint16_t slaveAddr, uint16_t memAddr, uint8_t data) { uint16_t timeout I2C_TIMEOUT; // 1. 等待总线空闲 while(I2caRegs.I2CSTR.bit.BB timeout--) { DELAY_US(1); } if(timeout 0) return I2C_ERROR_BUS_BUSY; // 2. 配置为主发送器设置STT位启动传输 // 发送流程S 从地址(W) 内存地址高8位 内存地址低8位 数据 // 先设置数据计数寄存器从地址(1) 内存地址(2) 数据(1) 4个字节 I2caRegs.I2CCNT 4; // 配置为主设备、发送模式、产生START、非重复模式、7位地址 I2caRegs.I2CMDR.all 0x6E20; // STT1, TRX1(发送), MST1, IRS1, STP0(稍后手动停止) // 3. 将要发送的数据按顺序写入I2CDXR // 注意写入顺序必须与总线上出现的顺序一致 // 先写7位从地址 写位 (0) I2caRegs.I2CDXR (slaveAddr 1) | 0x00; // 写方向 // 等待发送缓冲器就绪 (XRDY1) 或 仲裁丢失/无应答错误 timeout I2C_TIMEOUT; while((!I2caRegs.I2CSTR.bit.XRDY) timeout--) { if(I2caRegs.I2CSTR.bit.AL || I2caRegs.I2CSTR.bit.NACK) { // 处理错误... I2caRegs.I2CMDR.bit.STP 1; // 发送STOP清理总线 return I2C_ERROR_ARB_LOST_OR_NACK; } DELAY_US(1); } if(timeout 0) return I2C_ERROR_TIMEOUT; // 写入内存地址高字节 I2caRegs.I2CDXR (memAddr 8) 0xFF; // ... 同样等待XRDY并检查错误 // 写入内存地址低字节 I2caRegs.I2CDXR memAddr 0xFF; // ... 等待XRDY并检查错误 // 写入要存储的数据字节 I2caRegs.I2CDXR data; // ... 等待XRDY并检查错误 // 4. 等待数据传输完成 (ARDY1) timeout I2C_TIMEOUT; while((!I2caRegs.I2CSTR.bit.ARDY) timeout--) { DELAY_US(1); } if(timeout 0) return I2C_ERROR_TIMEOUT; // 5. 发送STOP条件结束本次传输 I2caRegs.I2CMDR.bit.STP 1; // 等待STOP条件完成 (BB0) timeout I2C_TIMEOUT; while(I2caRegs.I2CSTR.bit.BB timeout--) { DELAY_US(1); } // 6. EEPROM需要页写时间典型5ms延时等待写入完成 DELAY_US(5000); return I2C_SUCCESS; } // 从EEPROM指定地址读取一个字节 uint16_t I2C_ReadByte(uint16_t slaveAddr, uint16_t memAddr, uint8_t *data) { // 第一阶段发送内存地址写操作 // 设置数据计数为3从地址W 内存地址高 低 I2caRegs.I2CCNT 3; I2caRegs.I2CMDR.all 0x6E20; // STT1, TRX1, MST1, STP0 I2caRegs.I2CDXR (slaveAddr 1) | 0x00; // 写命令 // ... 等待XRDY发送内存地址高、低字节流程同上省略错误检查 // 等待第一阶段传输完成 (ARDY) while(!I2caRegs.I2CSTR.bit.ARDY); // 第二阶段重新START发送读命令读取数 // 设置数据计数为2从地址R 要读取的数据 I2caRegs.I2CCNT 2; // 注意这里STT位再次置1会产生一个重复START条件而不是STOP后再START // TRX0 设置为接收模式 I2caRegs.I2CMDR.all 0x2C20; // STT1, TRX0(接收), MST1, STP0 // 发送从地址 读位 I2caRegs.I2CDXR (slaveAddr 1) | 0x01; // 读命令 // 等待接收缓冲器就绪 (RRDY1) - 第一个RRDY对应的是地址的ACK不是数据 // 我们需要等待第二个RRDY即数据字节接收完成 // 更可靠的方法是等待ARDY计数结束或使用FIFO中断 while(!I2caRegs.I2CSTR.bit.ARDY); // 在非重复模式下当I2CCNT倒数到1时模块会自动对最后一个字节发送NACK // 倒数到0时如果STP1则产生STOP这里我们STP0所以需要手动发送STOP I2caRegs.I2CMDR.bit.STP 1; // 现在可以从I2CDRR读取数据 *data I2caRegs.I2CDRR; return I2C_SUCCESS; }注意事项上述查询例程为了清晰省略了部分错误处理和超时判断。实际产品代码中必须对每个等待状态XRDY RRDY ARDY BB添加超时机制并妥善处理仲裁丢失AL、无应答NACK等错误否则一旦从设备掉线或总线异常程序将死锁。使用FIFO和中断是更高效、更可靠的做法。3.3 关键寄存器SCIPRI调试时的“定海神针”在项目开发中我们经常需要连接仿真器如TI的XDS100/200进行在线调试。当你设置断点、单步执行时CPU核心会暂停这就是所谓的“仿真挂起事件”。此时一个关键问题出现了正在进行的I2C通信怎么办如果外设I2C模块也随CPU立刻停止可能会在总线上留下一个不完整的时序比如SCL被拉低后不再释放导致总线锁死影响其他设备甚至需要断电复位才能恢复。F2837xD的SCIPRI寄存器虽然名字带SCI但原理相通I2C有类似机制或需参考系统级仿真控制就是为了解决这个问题。它决定了当仿真挂起事件发生时外设的行为。其核心字段是FREESOFT位00b (立即停止)外设立即停止。不推荐用于I2C等实时性强的外设可能导致总线状态异常。01b (完成当前收/发序列后停止)外设会完成当前正在进行的字节传输比如一个8位数据加ACK位后再停止。这是最安全、最常用的设置。它保证了总线不会在比特位中间被“冻住”避免锁死。10b 或 11b (自由运行)外设完全不受仿真器暂停影响继续运行。这适用于那些即使CPU暂停也必须持续工作的场景如PWM生成电机驱动但对于调试I2C通信过程可能造成困扰因为数据会继续收发而你无法观察中间状态。如何配置通常在系统初始化阶段我们会将FREESOFT配置为01b。对于C2000系列你可能需要在系统级仿真控制寄存器如PCLKCR0相关的调试控制位或具体外设的仿真控制位中进行配置。虽然你提供的资料片段中SCIPRI寄存器属于SCI模块但I2C模块有类似的仿真控制逻辑需要查阅芯片的“System Control and Interrupts”章节找到类似DEVCFG或PARTID附近的仿真控制寄存器。实操建议在调试任何涉及实时通信I2C SPI SCI的项目前先确认这些外设在仿真挂起时的行为是否被正确配置为“完成当前操作后停止”。这能为你省去大量因总线锁死而不得不硬重启的麻烦。4. 高级应用与深度避坑指南4.1 FIFO与DMA的协同释放CPU的利器当需要传输大量数据时频繁的字节级中断会消耗大量CPU资源。F2837xD的I2C FIFO与DMA直接存储器访问结合可以完美解决这个问题。配置思路初始化FIFO设置合理的发送和接收中断触发级别。例如设置为8这样当发送FIFO剩余空间大于8个字节即已发送少于8个时触发TX中断去填充当接收FIFO中数据超过8个字节时触发RX中断去读取。配置DMA将DMA通道与I2C的发送/接收缓冲区关联。例如DMA的源地址是内存中的一个数组目的地址是I2CDXR发送或源地址是I2CDRR目的地址是内存数组接收。触发逻辑可以使能I2C的FIFO中断但在中断服务函数中不直接处理数据而是触发DMA传输。甚至可以利用DMA的自动触发功能如果支持在FIFO到达特定水平时自动启动DMA。示例代码片段概念性// 设置发送FIFO中断触发级别为12即FIFO中数据少于4个时触发 I2caRegs.I2CFFTX.bit.TXFFIL 12; I2caRegs.I2CFFTX.bit.TXFFIENA 1; // 使能TX FIFO中断 // 在I2C TX FIFO中断服务函数中 __interrupt void i2cTxFifoIsr(void) { // 检查是否是TXFFINT标志 if(I2caRegs.I2CFFTX.bit.TXFFINT 1) { // 不直接写数据而是触发或配置DMA通道开始从内存向I2CDXR搬运数据 DmaRegs.CHx.CONTROL.bit.RUN 1; // 启动DMA通道x // 清除中断标志 I2caRegs.I2CFFTX.bit.TXFFINTCLR 1; } // ... 清除PIE中断应答 }这样做的好处是CPU只需要在DMA传输开始和结束时介入中间成百上千个字节的传输完全由DMA和I2C硬件协作完成CPU可以处理其他任务或进入低功耗模式。4.2 多主竞争与仲裁丢失处理在有多于一个主设备的系统中仲裁是常态。F2837xD的I2C模块能自动检测仲裁丢失I2CSTR.AL位会被置1。当你的主设备在尝试输出‘1’释放SDA但检测到总线为‘0’被其他主设备拉低时就发生了仲裁丢失。发生仲裁丢失后硬件会自动立即从主设备模式切换到从设备模式MST位清零。停止驱动SDA和SCL。产生仲裁丢失中断如果使能。软件必须处理在中断服务函数或主循环中检测AL位。清除AL标志通常向该位写1清零。根据应用逻辑决定下一步是等待总线空闲后重试还是放弃本次传输。如果需要重试需要重新将模块配置为主设备设置MST1并重新启动传输流程设置STT1。if(I2caRegs.I2CSTR.bit.AL) { // 1. 清除仲裁丢失标志 I2caRegs.I2CSTR.bit.AL 1; // 写1清零 // 2. 可选发送STOP条件清理本机状态如果之前启动了 I2caRegs.I2CMDR.bit.STP 1; // 3. 等待总线空闲 while(I2caRegs.I2CSTR.bit.BB); // 4. 重新配置为主设备准备重试 I2caRegs.I2CMDR.bit.MST 1; // ... 重试发送START和地址/数据 }4.3 常见问题排查与实战技巧通信完全无响应SCL/SDA一直为高检查硬件首先用万用表测量SCL和SDA线对地电压正常应为电源电压通过上拉电阻。确认上拉电阻已正确焊接阻值合适。检查设备电源和地是否连接良好。检查引脚配置确认GPIO已正确复用为I2C功能并且配置为开漏模式对于C2000通常是配置为带内部上拉的异步输入硬件模块会自动控制输出。检查模块使能确认I2CMDR.IRS位已设置为1。这是最容易被忽略的一点能发送起始条件和地址但收不到ACKNACK从设备地址错误确认7位地址是否正确是否左移了一位。用逻辑分析仪抓取波形看发出的地址是否与从设备手册一致。注意许多设备的地址末尾位是可配置的通过硬件引脚务必核对。从设备未就绪例如EEPROM正在执行内部写周期典型5ms此时它会拉低SDA发送NACK。必须等待足够时间总线竞争或从设备故障其他设备可能正在驱动总线或者从设备已损坏。通信时好时坏偶尔数据错误时钟频率过高用示波器测量SCL实际频率和波形。如果上升沿过于缓慢由于总线电容过大、上拉电阻过大在高频下可能无法满足数据建立/保持时间要求。尝试降低I2C时钟频率增大ICCL/ICCH值。电源噪声检查电源纹波。模拟传感器对电源噪声很敏感可能导致通信错误。在I2C电源引脚附近增加去耦电容如100nF。软件时序问题在查询模式下检查状态位后是否留有足够的时间延迟再操作下一个寄存器。尤其是在连续写入I2CDXR时必须等待XRDY置位后再写下一个字节。使用FIFO时数据丢失或顺序错乱FIFO指针未复位在每次传输开始前特别是切换发送/接收模式后务必复位TX FIFO和RX FIFOSCIFFTX.TXFIFO和SCIFFRX.RXFIFO位。中断触发级别设置不当如果TXFFIL设置得太低可能中断过于频繁设置得太高则可能在FIFO完全排空后才有中断造成发送间隙。需要根据总线速度和CPU处理能力权衡。DMA与FIFO的协同问题如果使用DMA确保DMA的传输数据量Burst Size与FIFO深度以及I2C传输的字节数匹配避免DMA传输完了I2C还没发完或者反之。调试时总线锁死SCL被持续拉低从设备“死机”某些从设备在异常情况下如电压不稳、错误指令可能会拉低SCL以延长时钟周期但之后未能释放。这是I2C协议允许的“时钟拉伸”但设备如果“拉伸”后不恢复就会锁死总线。解决方案软件恢复尝试作为主设备在软件控制下产生多个SCL时钟脉冲通常9个以上同时监测SDA直到从设备释放总线。这需要将I2C模块的SCL引脚临时配置为GPIO输出模式手动模拟时钟。硬件复位如果软件恢复无效最直接的方法是循环通断从设备的电源如果设计允许或者复位整个系统。预防确保电源稳定通信协议符合从设备要求并如前所述正确配置仿真控制寄存器如FREESOFT位避免调试时产生异常时序。最后的小技巧投资一个逻辑分析仪即使是便宜的USB款配合诸如PulseView或Saleae Logic的软件可以直观地捕获I2C波形显示起始位、地址、数据、ACK/NACK。这是调试I2C问题无可替代的神器能让你迅速定位问题是出在硬件、配置还是软件逻辑上。