从波形到中断一篇看懂I2C通信原理、地址、ACK与调试方法一、本文适用场景本文适用于以下学习和调试场景正在学习STM32、GD32或其他单片机的I2C外设会调用I2C库函数但看不懂底层通信过程不清楚SCL和SDA应该在什么时候变化不理解START、STOP、ACK和NACK的波形特征不清楚7位地址、8位地址、读地址和写地址之间的关系不明白I2C发送一个字节后为什么会产生第9个时钟不清楚SBSEND、ADDSEND、TBE、RBNE、BTC和AERR等标志的含义使用逻辑分析仪抓取I2C波形但不知道应该按照什么顺序分析遇到地址后NACK、总线BUSY、SDA被拉低、接收中断频繁触发等问题。本文将从I2C的基础采样规则开始逐步讲解一帧通信的完整流程并把代码中的状态标志与逻辑分析仪上的实际波形对应起来。二、I2C总线基础I2C是一种同步串行通信协议常用于单片机与传感器、EEPROM、电源管理芯片、风扇控制器、显示屏等器件之间的通信。I2C总线主要由两根信号线组成SCLSerial Clock串行时钟线SDASerial Data串行数据线。其中主机通常负责产生SCL时钟SDA用于传输地址、读写方向、数据以及ACK/NACK应答信号。I2C最核心的采样规则是SCL低电平期间SDA可以变化。 SCL高电平期间SDA应保持稳定。发送方一般会在SCL为低电平时修改SDA为下一位数据做好准备。接收方则在SCL上升沿附近或者SCL高电平的稳定区域内读取SDA。因此在分析I2C波形时应该重点观察每一个SCL上升沿附近的SDA电平SDA为高电平表示逻辑1 SDA为低电平表示逻辑0图1 I2C基础与采样原则从图中可以看到SCL低电平期间SDA可以发生变化SCL进入高电平后SDA需要保持稳定接收方根据SDA的高低电平还原数据。需要注意的是START和STOP属于特殊情况。它们允许SDA在SCL高电平期间发生变化用来表示一次通信的开始和结束。三、I2C总线空闲状态在没有设备进行通信时I2C总线通常处于空闲状态。总线空闲时SCL 高电平 SDA 高电平这是因为I2C的SCL和SDA通常采用开漏输出结构信号线依靠外部上拉电阻恢复为高电平。主机在发起一次新的通信之前通常需要先检查总线是否空闲。在部分MCU中可以通过BUSY标志判断I2C总线状态。示例while(i2c_flag_get(I2C0,I2C_FLAG_I2CBSY)){/* 等待I2C总线空闲 */}如果总线一直处于BUSY状态可能存在以下问题上一次通信没有正常发送STOPSDA被某个从机持续拉低SCL被某个设备持续拉低主机在通信过程中异常复位GPIO复用配置错误I2C外设没有正确初始化BUSY状态没有被正确恢复。四、START起始信号当主机准备发起一次I2C通信时需要先产生START起始信号。START的产生条件是SCL保持高电平时SDA由高电平变为低电平。也就是SCL 1 SDA1 → 0因为正常数据传输时SDA不应该在SCL高电平期间变化所以从机一旦检测到这种特殊变化就知道一次新的通信开始了。START产生后主机通常会继续发送7位从机地址 R/W读写方向位在部分GD32或STM32系列MCU中程序设置START位后I2C硬件会自动产生START波形。示例i2c_start_on_bus(I2C0);然后等待START发送完成标志while(!i2c_flag_get(I2C0,I2C_FLAG_SBSEND)){}其中SBSEND通常表示START起始条件已经成功发送。五、STOP停止信号当主机完成地址和数据传输后通常会产生STOP停止信号释放I2C总线。STOP的产生条件是SCL保持高电平时SDA由低电平变为高电平。也就是SCL 1 SDA0 → 1START和STOP可以通过下面的方法快速记忆SCL高电平时SDA高变低START SCL高电平时SDA低变高STOP在程序中主机通常通过设置STOP位让I2C硬件自动产生停止波形。示例i2c_stop_on_bus(I2C0);产生STOP以后SCL和SDA最终都应该恢复为高电平总线重新进入空闲状态。六、ACK和NACK是什么I2C每发送8位地址或数据后还会额外产生第9个时钟。前8个时钟用于传输一个完整字节第9个时钟用于传输ACK或NACK应答信号。1. ACK应答ACK表示接收方已经正确接收到前面的地址或数据。在第9个时钟期间接收方主动将SDA拉低第9个时钟期间 SDA 0这就是ACK。ACK可以理解为接收方告诉发送方当前字节已经收到可以继续通信。2. NACK非应答NACK表示接收方没有应答或者接收方不准备继续接收后续数据。在第9个时钟期间如果没有设备把SDA拉低SDA保持高电平第9个时钟期间 SDA 1这就是NACK。3. ACK和NACK由谁产生ACK或NACK由当前字节的接收方产生。主机向从机写数据时发送方主机 接收方从机 ACK/NACK由从机产生主机从从机读取数据时发送方从机 接收方主机 ACK/NACK由主机产生连续读取多个字节时主机通常对前面的数据返回ACK表示还要继续读取。读取最后一个字节后主机返回NACK表示当前字节是最后一个字节不需要继续发送。随后主机产生STOP结束本次通信。图2 I2C的START、STOP、ACK与NACK从图中可以看到START和STOP都发生在SCL高电平期间。每发送8位地址或数据后第9个时钟用于传输ACK或NACK。七、为什么发送8位数据后还有第9个时钟I2C以字节为基本传输单位。一个完整字节包含8位数据D7 D6 D5 D4 D3 D2 D1 D0发送完D0后主机还会产生第9个时钟用于让接收方返回应答。完整过程如下第1个时钟D7 第2个时钟D6 第3个时钟D5 第4个时钟D4 第5个时钟D3 第6个时钟D2 第7个时钟D1 第8个时钟D0 第9个时钟ACK或NACK因此第9位不是数据位而是应答位。在分析逻辑分析仪波形时不要把第9个时钟误认为下一个字节的数据。八、7位地址和8位地址的关系I2C地址是开发过程中最容易混淆的部分之一。很多芯片手册给出的是7位地址但驱动代码、寄存器或者逻辑分析仪中显示的可能是8位地址字节。一个完整的I2C地址字节由以下两部分组成A6 A5 A4 A3 A2 A1 A0 R/W其中A6A07位从机地址 R/W读写方向位读写方向位的定义为R/W 0写操作 R/W 1读操作因此总线上真正发送的地址字节本质上是7位从机地址左移1位再拼接R/W位。九、地址0x20为什么会变成0x40和0x41假设某个I2C设备的7位地址为0x201. 写地址字节写操作时R/W位为0。计算方式0x201计算结果0x40所以写地址字节为0x402. 读地址字节读操作时R/W位为1。计算方式(0x201)|0x01计算结果0x41所以读地址字节为0x41三者之间的关系如下7位设备地址0x20 写地址字节0x40 读地址字节0x41对应的二进制形式为0x20 010 0000 0x40 0100 0000 0x41 0100 0001代码示例uint8_tslave_addr0x20;uint8_twrite_addrslave_addr1;uint8_tread_addr(slave_addr1)|0x01;运行结果write_addr 0x40 read_addr 0x41图3 I2C的7位地址、写地址和读地址从图中可以看到0x40和0x41通常不是两个不同的I2C设备地址而是同一个7位地址0x20在写操作和读操作下的两种总线表示。十、地址到底要不要左移地址是否需要左移取决于具体驱动接口的要求。有些驱动接口要求调用者传入7位地址。例如i2c_write(0x20,data,length);这种情况下驱动内部可能会自动完成地址左移1位 拼接R/W方向位有些底层接口则要求调用者直接传入完整的地址字节。例如i2c_master_addressing(I2C0,0x40,I2C_TRANSMITTER);如果接口内部已经会自动处理地址而调用者又提前左移一次就会导致地址错误。例如原始7位地址为0x20调用者错误地先转换为0x40驱动内部又执行一次左移0x40 1 0x80这样总线上发送的地址就会错误从机无法返回ACK。调试地址问题时需要确认以下内容芯片手册给出的是7位地址还是8位地址字节驱动函数要求传入7位地址还是完整地址字节驱动内部是否会自动左移驱动内部是否会自动拼接R/W位逻辑分析仪显示的是7位地址还是原始地址字节。十一、主机写一帧数据的完整流程一次基本的主机写操作通常包含以下步骤1. 等待总线空闲 2. 产生START 3. 发送从机地址和写方向位 4. 等待从机ACK 5. 发送第1个数据字节 6. 等待从机ACK 7. 继续发送后续数据字节 8. 每个字节后等待ACK 9. 产生STOP假设从机7位地址为0x20主机准备发送两个数据字节。通信过程可以表示为START 0x40 ACK DATA1 ACK DATA2 ACK STOP其中0x40 0x20 10x40的最低位为0表示当前为写操作。每发送一个字节后从机都会通过ACK告诉主机当前字节已经接收完成可以继续发送。如果地址发送后立即出现NACK应优先检查地址是否正确是否混淆7位地址和8位地址地址是否被重复左移读写方向位是否正确从机是否上电从机是否完成初始化SDA和SCL是否接反总线上是否存在上拉电阻从机是否处于可通信状态。十二、主机读一帧数据的完整流程一次基本的主机读操作通常包含以下步骤1. 等待总线空闲 2. 产生START 3. 发送从机地址和读方向位 4. 等待从机ACK 5. 从机发送数据 6. 主机返回ACK或NACK 7. 产生STOP从地址为0x20的设备读取一个字节时可以表示为START 0x41 ACK DATA NACK STOP其中0x41 (0x20 1) | 0x01主机读取最后一个字节后需要返回NACK告诉从机读取已经结束不需要继续发送数据。随后主机产生STOP。连续读取多个字节假设主机连续读取3个字节通信过程通常为START 读地址 ACK DATA1 主机ACK DATA2 主机ACK DATA3 主机NACK STOP前两个字节后主机返回ACK表示继续读取。最后一个字节后主机返回NACK表示当前读取结束。十三、为什么读取寄存器时经常要先写后读很多I2C从机内部包含多个寄存器。主机在读取某个寄存器之前必须先告诉从机我要读取哪个寄存器。因此常见的寄存器读取流程分为两个阶段。1. 阶段A发送寄存器地址START 从机写地址 ACK 寄存器地址或命令字 ACK2. 阶段B读取寄存器数据Repeated START 从机读地址 ACK 从机返回数据 主机NACK STOP完整流程如下START 从机写地址 ACK 寄存器地址 ACK Repeated START 从机读地址 ACK 数据 NACK STOPRepeated START称为重复起始信号。它可以在不释放总线的情况下重新发起一次地址传输并将通信方向从写切换为读。部分设备也允许下面的通信方式START 写地址 寄存器地址 STOP 延时一段时间 START 读地址 读取数据 STOP到底使用STOP还是Repeated START需要查看具体从机芯片的数据手册和通信协议。图4 一帧I2C通信的完整流程从总线空闲开始主机依次产生START、发送地址、等待ACK、发送或接收数据最后产生STOP。对于寄存器查询类命令通常需要先写入寄存器地址再发起读取。十四、一帧主机写通信的代码流程下面以主机发送数据为例说明代码中的主要步骤。/* 等待总线空闲 */while(i2c_flag_get(I2C0,I2C_FLAG_I2CBSY)){}/* 产生START */i2c_start_on_bus(I2C0);/* 等待START发送完成 */while(!i2c_flag_get(I2C0,I2C_FLAG_SBSEND)){}/* 发送从机地址方向为写 */i2c_master_addressing(I2C0,slave_addr,I2C_TRANSMITTER);/* 等待地址发送完成 */while(!i2c_flag_get(I2C0,I2C_FLAG_ADDSEND)){}/* 清除地址发送完成标志 */i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);/* 发送数据 */for(uint8_ti0;ilength;i){while(!i2c_flag_get(I2C0,I2C_FLAG_TBE)){}i2c_data_transmit(I2C0,buffer[i]);}/* 等待最后一个字节传输完成 */while(!i2c_flag_get(I2C0,I2C_FLAG_BTC)){}/* 产生STOP */i2c_stop_on_bus(I2C0);需要注意不同MCU和不同固件库的函数名、标志名以及清除方式可能不同应以当前芯片的参考手册和库函数说明为准。十五、I2C中断是怎么产生的I2C中断并不是总线上每出现一次电平变化CPU就进入一次中断。SCL时钟、地址移位和数据移位通常由I2C硬件外设自动完成。CPU是否进入中断一般取决于两个条件对应状态标志位置位 并且 对应中断使能位已经打开可以简化理解为中断请求 状态标志有效 对应中断已使能例如发送缓冲区为空时TBE标志可能置位。只有在TBE中断被使能的情况下才会向CPU请求进入对应的I2C中断服务程序。因此不应该简单理解成SCL每跳变一次CPU就进入一次中断。SCL的每一个高低电平变化一般都是由I2C硬件自动产生的。十六、常见I2C状态和中断标志在GD32、STM32等MCU中经常可以看到以下I2C状态标志SBSEND ADDSEND TBE RBNE BTC AERR不同芯片中的名称和具体定义可能略有区别最终应以当前MCU参考手册为准。1. SBSENDSBSEND通常表示START起始条件已经发送完成。对应波形为SCL高电平时SDA由高变低。主机检测到SBSEND后通常可以继续发送从机地址。代码示例if(i2c_flag_get(I2C0,I2C_FLAG_SBSEND)){i2c_master_addressing(I2C0,slave_addr,I2C_TRANSMITTER);}2. ADDSEND在主机模式下ADDSEND通常表示地址阶段已经完成并且从机已经返回ACK。在从机模式下ADDSEND也可能表示从机地址已经匹配。主机地址发送后如果从机没有返回ACK程序通常不会正常进入ADDSEND流程而可能产生AERR应答错误。3. TBETBE通常表示发送数据寄存器为空可以写入下一个字节。代码示例if(i2c_flag_get(I2C0,I2C_FLAG_TBE)){i2c_data_transmit(I2C0,tx_buffer[index]);index;}需要注意TBE主要表示发送数据寄存器可以继续写入。它不一定表示当前字节连同ACK阶段都已经完全结束。4. BTCBTC通常表示当前字节以及对应的应答阶段已经完成。发送最后一个字节后程序经常等待BTC再产生STOP。代码示例while(!i2c_flag_get(I2C0,I2C_FLAG_BTC)){}i2c_stop_on_bus(I2C0);TBE和BTC的含义不能完全等同TBE发送数据寄存器为空可以装入下一个字节。 BTC当前字节以及应答阶段已经完成。5. RBNERBNE通常表示接收数据寄存器非空已经收到一个新字节。程序需要及时读取接收数据寄存器。代码示例if(i2c_flag_get(I2C0,I2C_FLAG_RBNE)){rx_buffer[index]i2c_data_receive(I2C0);index;}读取数据寄存器后RBNE通常会被清除。如果RBNE置位后一直不读取数据可能导致中断持续触发接收缓冲区溢出CPU频繁进入中断其他任务无法及时运行系统表现为卡死或响应变慢。6. AERRAERR通常表示地址或数据发送后没有收到接收方的ACK。地址阶段出现AERR时应重点检查从机地址是否正确地址是否被重复左移读写方向位是否正确从机是否已经上电从机是否在线SDA和SCL接线是否正确总线上拉电阻是否存在从机是否处于可响应状态。图5 I2C状态标志与实际波形阶段的对应关系SBSEND对应START发送完成ADDSEND对应地址阶段完成TBE表示发送寄存器可以写入新数据RBNE表示接收到新字节BTC表示字节传输完成AERR通常表示没有收到ACK。十七、发送一个字节是否一定进入一次中断答案是不一定。在很多简单的I2C中断发送程序中可能表现为发送一个字节 TBE置位 进入I2C中断 装载下一个字节因此看起来像是发送一个字节就进入一次中断。但是实际中断次数还与以下因素有关MCU的I2C外设结构启用了哪些中断源是否使用发送FIFO是否使用接收FIFO是否使用DMA多个状态是否共用一个中断入口中断服务程序一次处理几个状态中断进入之前是否已经有多个标志同时置位。CPU进入一次I2C中断后可能会同时检查多个状态标志。例如voidI2C0_EV_IRQHandler(void){if(i2c_flag_get(I2C0,I2C_FLAG_SBSEND)){/* 发送地址 */}if(i2c_flag_get(I2C0,I2C_FLAG_ADDSEND)){/* 处理地址发送完成 */}if(i2c_flag_get(I2C0,I2C_FLAG_TBE)){/* 发送下一个字节 */}if(i2c_flag_get(I2C0,I2C_FLAG_RBNE)){/* 读取接收数据 */}}因此更准确的理解是I2C硬件在不同通信阶段设置状态标志。 当对应中断被使能时CPU才可能进入中断服务程序。不能简单认为I2C总线上出现一个动作就一定对应一次固定中断。十八、逻辑分析仪抓取I2C波形的正确顺序拿到一段I2C波形后不建议一开始就直接分析数据内容。更合理的分析顺序如下。1. 检查SCL是否正常首先观察SCL时钟是否连续频率是否符合预期高低电平是否正常是否存在异常拉长的低电平是否存在明显毛刺上升沿和下降沿是否过慢。如果SCL本身不正常后续分析地址和数据通常没有意义。2. 寻找START和STOPSTART的特征为SCL高电平时SDA由高变低。STOP的特征为SCL高电平时SDA由低变高。先确定一帧通信从哪里开始到哪里结束。3. 识别地址字节和R/W位START之后的第一个字节通常为地址字节。需要确认高7位是否为目标从机地址最低位是读还是写工具显示的是7位地址还是完整地址字节。4. 检查第9个时钟的ACK或NACK每发送一个地址或数据字节后都要检查第9个时钟。如果地址后立即NACK应优先检查地址电源接线上拉电阻从机状态。5. 分析数据内容确认地址和ACK正常后再分析实际数据数据顺序是否正确字节长度是否正确大小端是否正确命令字是否正确返回数据是否符合协议是否包含CRC或PEC字节间隔是否满足协议要求。十九、地址后一直NACK怎么排查如果逻辑分析仪显示地址后一直NACK可以按照下面的顺序排查。1. 检查地址格式确认当前使用的是7位地址 还是 8位地址字节例如设备7位地址为0x207位地址0x20 写地址0x40 读地址0x41不要把0x40再次左移。2. 检查R/W方向如果当前准备向从机写入命令地址最低位应该为0。如果当前准备从从机读取数据地址最低位应该为1。3. 检查从机供电确认从机电源电压正常从机地线与主机共地从机复位引脚状态正常从机已经完成上电初始化。4. 检查SDA和SCL接线确认SDA连接到SDASCL连接到SCL没有接反引脚复用配置正确GPIO没有被其他模块占用。5. 检查上拉电阻确认SDA和SCL是否连接了合适的上拉电阻。没有上拉电阻时I2C总线可能无法正常恢复高电平。6. 检查从机地址配置部分从机具有地址配置引脚例如A0 A1 A2这些引脚的高低电平会影响最终I2C地址。需要根据原理图和数据手册确认实际地址。二十、SDA一直被拉低怎么排查SDA一直为低电平通常表示总线没有正常释放。可能原因包括从机状态机卡在未完成的通信中上一次通信没有产生STOP主机在通信过程中异常复位从机异常拉住SDASDA线路短路GPIO模式配置错误引脚复用配置错误从机仍在等待后续时钟。部分情况下可以通过GPIO手动发送若干个SCL时钟帮助从机退出未完成的接收状态。常见恢复思路如下1. 临时把SCL配置为GPIO输出。 2. 保持SDA释放。 3. 手动输出9个左右的SCL脉冲。 4. 尝试产生STOP。 5. 重新初始化I2C外设。该方法是否适用需要结合具体从机协议和硬件情况判断。二十一、总线一直BUSY怎么排查总线一直BUSY可能由以下原因引起SDA没有恢复高电平SCL被某个器件拉低上一次通信异常结束STOP没有正确产生I2C外设状态异常GPIO复用配置错误主机初始化顺序不正确从机处于异常状态。建议按照下面的顺序检查1. 用逻辑分析仪观察SDA和SCL实际电平。 2. 检查SDA是否被持续拉低。 3. 检查SCL是否被持续拉低。 4. 检查上一次通信是否存在STOP。 5. 检查GPIO复用和开漏配置。 6. 尝试复位I2C外设。 7. 必要时执行总线恢复操作。二十二、为什么I2C需要上拉电阻I2C的SDA和SCL通常采用开漏输出结构。开漏输出的特点是设备可以主动输出低电平。 设备通常不能主动输出高电平。信号线恢复为高电平需要依靠外部上拉电阻。上拉电阻会影响信号上升沿速度总线最高通信频率电平稳定性总线功耗设备拉低信号时的电流多设备通信可靠性。常见的I2C上拉电阻可能为2.2kΩ 4.7kΩ 10kΩ但实际阻值不能只靠经验固定选择需要结合以下因素I2C总线电压总线电容PCB走线长度从机数量通信频率芯片允许的低电平灌电流。上拉电阻过大时信号上升沿可能过慢。上拉电阻过小时设备把总线拉低时的电流会增大。逻辑分析仪主要观察数字逻辑状态。如果怀疑信号存在上升沿过慢、振铃、过冲或者电压幅值异常建议使用示波器观察真实模拟波形。二十三、波形正常但数据错误怎么排查如果START、地址、ACK和STOP看起来都正常但数据内容不符合预期可以重点检查以下内容命令字是否正确寄存器地址是否正确数据长度是否一致字节顺序是否正确大小端是否正确是否存在CRC是否存在PEC校验和计算是否正确数据结构是否存在对齐问题主机和从机使用的协议版本是否一致连续读取时寄存器是否自动递增是否需要等待从机处理命令写命令后是否需要延时再读取结果。例如一个16位数据可能按下面两种顺序发送。大端格式高字节 低字节小端格式低字节 高字节如果主从双方对大小端理解不一致就会出现波形正常但数据错误的情况。二十四、如何把代码、标志位和波形对应起来调试I2C时建议同时观察以下三类信息。1. 软件执行流程等待BUSY清除 产生START 等待SBSEND 发送地址 等待ADDSEND 发送数据 等待TBE或BTC 产生STOP2. I2C状态标志SBSEND ADDSEND TBE BTC RBNE AERR3. 逻辑分析仪波形START 地址 ACK 数据 ACK STOP把三者对应起来后问题会更容易定位。例如程序一直等待ADDSEND而逻辑分析仪显示地址后为NACK。这时问题通常不在等待循环本身而更可能是从机地址错误地址格式错误从机没有上电从机没有应答SDA或SCL连接异常上拉电阻异常。再例如程序频繁进入RBNE中断但没有读取接收数据寄存器。这可能导致RBNE持续保持CPU频繁进入中断接收缓冲区溢出其他任务得不到执行系统表现为卡死。因此分析I2C问题时不能只看代码也不能只看逻辑分析仪。最有效的方法是程序执行流程 I2C状态标志 实际总线波形三者同时进行对照。二十五、I2C调试的推荐步骤实际项目中可以按照以下顺序排查I2C问题。第1步确认从机供电和共地正常。 第2步确认SDA、SCL接线和GPIO复用正确。 第3步确认总线上存在上拉电阻。 第4步确认总线空闲时SDA、SCL都为高电平。 第5步检查SCL频率是否符合从机要求。 第6步寻找START和STOP。 第7步确认地址字节和R/W位。 第8步检查地址后的ACK或NACK。 第9步检查每个数据字节后的ACK或NACK。 第10步检查数据顺序、长度和校验。 第11步把波形与代码中的状态标志对应起来。 第12步必要时使用示波器检查信号质量。不要一开始就直接分析数据内容。如果地址阶段已经NACK后面的数据实际上并没有被从机正常接收。二十六、核心知识总结学习I2C时可以先记住以下结论I2C主要由SCL和SDA两根信号线组成。SCL低电平期间SDA可以变化。SCL高电平期间SDA应保持稳定。SCL高电平时SDA由高变低表示START。SCL高电平时SDA由低变高表示STOP。每发送8位地址或数据后第9个时钟用于ACK或NACK。ACK表示接收方在第9个时钟期间把SDA拉低。NACK表示第9个时钟期间SDA保持高电平。7位地址左移1位后再拼接R/W位形成总线地址字节。7位地址0x20对应写地址字节0x40和读地址字节0x41。主机读取最后一个字节后通常返回NACK再产生STOP。查询寄存器时通常需要先写入寄存器地址再发起读取。SBSEND通常表示START已经发送完成。ADDSEND通常表示地址阶段已经完成。TBE表示发送数据寄存器为空可以写入下一个字节。RBNE表示接收数据寄存器非空需要及时读取数据。BTC通常表示当前字节及应答阶段已经完成。AERR通常表示地址或数据发送后没有收到ACK。I2C中断由状态标志和中断使能共同决定。发送一个字节可能触发一次中断但不是所有控制器都固定如此。调试波形时应按照SCL、START、地址、ACK、数据、STOP的顺序分析。地址后立即NACK应优先检查地址、电源、接线、上拉电阻和从机状态。二十七、结语真正掌握I2C不只是会调用发送和接收函数而是能够理解代码为什么停在当前状态 状态标志为什么会置位 总线上实际发生了什么波形当程序执行流程、I2C状态标志和逻辑分析仪波形能够一一对应时就可以快速判断问题发生在总线空闲阶段START阶段地址阶段ACK阶段数据阶段STOP阶段中断处理阶段。这也是从“会调用I2C接口”走向“能够独立调试I2C驱动”的关键一步。---