
1. 项目概述IIC总线这个在嵌入式世界里无处不在的“双线英雄”几乎每个工程师的职业生涯都绕不开它。从读取一颗温湿度传感器到配置一块复杂的音频编解码芯片再到管理一片EEPROMIIC以其简洁的两线制SDA数据线和SCL时钟线和灵活的多主多从架构成为了连接微控制器与低速外设的经典桥梁。然而简洁的背后是严谨的时序、复杂的仲裁逻辑和一堆需要精准配置的寄存器。很多新手在初次接触时往往被手册里大段的时序图和寄存器位描述搞得晕头转向配置出来的通信要么时好时坏要么干脆“沉默是金”。今天我们就以飞思卡尔现恩智浦的MC13234/MC13237这颗常用于无线传感网络的微控制器为例把IIC从最底层的物理原理到协议层的握手规则再到芯片内部寄存器的每一个关键配置位彻底掰开揉碎讲清楚。这不是一篇照本宣科的数据手册翻译而是结合了多年调试经验告诉你为什么这么配置以及配置错了会怎样。我们会深入其IIC模块的寄存器比如如何用IIC1F寄存器在16MHz总线时钟下精准调出100kbps的波特率IIC1C1里的MST和TX位在通信流程中如何像开关一样被拨动以及如何通过IIC1S寄存器解读总线上的“暗流涌动”比如仲裁丢失。无论你是正在学习嵌入式通信的学生还是需要为新产品调试IIC驱动的工程师这篇文章都将提供一套可直接“抄作业”的配置思路和排错指南。2. IIC总线协议核心原理深度拆解2.1 物理层线与逻辑与开漏输出IIC总线的物理层设计是其一切特性的基石。它仅使用两根线串行数据线SDA和串行时钟线SCL。所有设备都通过开漏Open-Drain或开集Open-Collector输出结构连接到这两根线上并通过上拉电阻Rp连接到正电源VDD。这种设计实现了“线与”Wired-AND功能。任何设备都可以通过将输出晶体管导通来将总线拉低输出逻辑0而释放总线输出晶体管关断时总线由上拉电阻拉高逻辑1。这意味着只要有一个设备输出0整条线就是0只有当所有设备都输出1即释放总线时总线才是1。这是实现多主仲裁和时钟同步的物理基础。在MC13234/37的数据手册中特别强调SDA和SCL引脚内部有一个保护二极管连接到VDD这意味着你绝对不能给这两个引脚施加高于VDD的电压否则可能损坏这个保护二极管甚至内部电路。上拉电阻Rp的选择是个经验活它需要在总线电容、通信速度和功耗之间取得平衡。总线电容所有设备引脚电容和走线寄生电容之和不能超过400pF否则上升沿会变得过于缓慢导致时序 violation。Rp值越小上升时间越快允许的通信速率越高但静态功耗也越大。通常在标准模式100kbps下对于几米内的通信4.7kΩ到10kΩ是常见选择。你可以用一个简单的公式估算上升时间 Tr ≈ 0.8 * Rp * CbCb为总线总电容要确保Tr小于SCL高电平周期的某个安全比例。2.2 协议层通信帧的“起承转合”一次完整的IIC通信就像一场有严格礼仪的对话由四个基本环节构成起始START、地址Address、数据Data和停止STOP。起始信号START与重复起始信号Repeated START当总线空闲SDA和SCL均为高电平时主设备通过产生一个START信号来发起通信。START信号定义为在SCL为高期间SDA线产生一个从高到低的跳变。这个独特的信号唤醒总线上所有从设备告诉它们“注意有主设备要讲话了”。而重复起始信号Sr则是在不发送STOP信号释放总线的情况下直接发起一个新的START。这允许主设备在连续通信中切换对话对象从设备或改变数据方向读/写而无需释放总线再重新竞争提高了总线利用效率。地址帧7位与10位寻址START信号后的第一个字节一定是地址帧。其高7位或高10位模式下的特定组合是从设备的地址最低位是R/W#位0表示主设备写1表示主设备读。MC13234/37的IIC模块支持两种模式7位地址模式IIC1C2.ADEXT0和10位地址模式IIC1C2.ADEXT1。在7位模式下地址直接存放在IIC1A寄存器的AD[7:1]位。在10位模式下过程稍复杂首先发送一个特殊的5位前缀11110加上10位地址的最高两位AD10, AD9和R/W#位此时为0表示写地址从设备比对前7位匹配的从设备回应ACK后主设备再发送地址的低8位AD[8:1]从设备再次比对并回应ACK这才完成寻址。10位地址极大地扩展了寻址空间但代价是增加了通信开销。数据帧与应答ACK/NACK地址帧之后的每个字节都是数据帧每个字节8位高位MSB先发。每个字节传输完成后接收方需要在第9个时钟脉冲期间将SDA拉低发出一个应答信号ACK表示“字节收到请继续”。如果接收方是主设备且在读取多个字节后不想再读或者从设备因故无法处理数据则会在第9个时钟周期保持SDA高电平发出非应答信号NACK这通常意味着“对话结束”。发送方无论是主还是从在收到NACK后就应该终止传输由主设备发出STOP或重复START。停止信号STOP主设备通过产生STOP信号来结束本次通信并释放总线。STOP信号定义为在SCL为高期间SDA线产生一个从低到高的跳变。注意STOP信号和MCU的低功耗模式指令STOP完全是两码事切勿混淆。2.3 多主模式的核心仲裁与时钟同步IIC协议最精妙的设计之一就是支持多主操作而这依赖于仲裁和时钟同步机制。时钟同步所有主设备都可以驱动SCL线。当多个主设备同时开始传输时它们的时钟需要通过“线与”逻辑进行同步。具体过程是每个设备在自已的时钟低电平期间将SCL拉低并开始计数低电平时间。只有当所有设备都结束了自己的低电平计数后SCL线才会被释放变高。因此同步后的SCL低电平周期等于所有主设备中最长的那个低电平周期而高电平周期等于最短的那个高电平周期。这就实现了时钟同步避免了总线冲突。数据仲裁在时钟同步的基础上当多个主设备同时发送数据时它们会在SDA线上进行“仲裁”。仲裁发生在SDA线被采样为高电平的期间即SCL为高时。每个主设备同时输出自己要发送的位并同时回读SDA线的状态。如果某个主设备输出为1释放总线但回读发现SDA线为0被其他设备拉低那么它就意识到自己“输”了立即停止驱动SDA线并切换到从接收模式监听赢得仲裁的主设备继续通信。仲裁的规则是输出0的优先级高于输出1。因为0是主动拉低而1是释放总线。这意味着在地址或数据阶段二进制数值更小的设备有更多0在前在仲裁中具有更高优先级。MC13234/37的IIC1S.ARBL位会在仲裁丢失时被硬件置1软件必须检测并处理此情况通常意味着本次传输失败需要重试。时钟拉伸这是从设备控制通信节奏的一种机制。从设备如果处理数据较慢可以在应答位第9个时钟或字节传输中的任何时刻在SCL为低时继续将其拉低强制主设备进入等待状态直到从设备释放SCL。这要求主设备的IIC驱动程序必须能处理SCL被意外拉长的情形而不是简单超时出错。3. MC13234/37 IIC模块寄存器精讲与配置实战理解了协议我们进入实战环节看看如何在MC13234/37这颗MCU上通过配置六个关键寄存器让IIC模块乖乖工作。这些寄存器位于直接页寄存器映射中地址从0x006A到0x006F。3.1 频率配置寄存器IIC1F设定通信的“心跳”IIC1F寄存器是配置IIC总线时序的核心它直接决定了SCL时钟的频率以及几个关键建立/保持时间。其公式来源于数据手册但手册的表述可能有些绕我们把它翻译成工程师能直接用的语言。寄存器有两个关键字段MULT[1:0]乘法因子和ICR[5:0]时钟速率。总线时钟Bus Clock通常是CPU时钟的一半对于MC13234/37典型值是16 MHz。IIC的波特率由以下公式决定IIC Baud Rate Bus Clock Frequency / (MULT * SCL_Divider)其中SCL_Divider的值由ICR查表获得见数据手册表13-6。举个例子我们要配置最常用的100kbps标准模式总线时钟为16MHz。翻阅手册表13-4提供了多组能达到100kbps的(MULT, ICR)组合。我们选取MULT2 (01b)ICR0x14这一组。计算验证查表13-6ICR0x14对应的SCL_Divider 80。则波特率 16,000,000 / (2 * 80) 100,000 bps完美。除了波特率IIC1F还通过查表决定了SDA_Hold_Time、SCL_Start_Hold_Time和SCL_Stop_Hold_Time。这些时间参数对于满足IIC协议规范、确保信号稳定性至关重要尤其是在总线负载较重电容大时。对于大多数应用如果你使用标准速度且走线不长直接使用手册推荐的100kbps配置组即可无需深究这些保持时间。但在高速模式400kbps甚至更高或长距离通信时可能需要根据实际波形调整ICR值来微调这些时间以补偿信号边沿的畸变。配置心得在实际项目中我通常先将IIC1F配置为已知可用的标准值如上述的0x14与MULT2。如果通信不稳定第一步不是去调这些保持时间而是用示波器测量SCL和SDA的实际波形看上升沿是否陡峭高低电平是否干净。往往问题出在上拉电阻过大或总线电容过大导致边沿过缓误触发了亚稳态。此时应优先优化硬件减小Rp缩短走线而非盲目调整软件参数。3.2 控制寄存器1IIC1C1模式与中断的“总开关”IIC1C1是控制IIC模块行为的主要寄存器每一位都至关重要。IICEN (Bit 7)IIC模块使能位。这是“总闸”必须在配置其他所有参数之后再置1。如果先使能再配置模块可能以不确定的状态开始工作导致总线冲突。IICIE (Bit 6)中断使能位。建议在初始化阶段先清零等所有配置完成、准备开始传输前再置1。避免在配置过程中产生不必要的中断。MST (Bit 5)主模式选择位。这是一个状态位但也由硬件自动管理。当软件发起START条件时硬件会自动将其置1当产生STOP条件或仲裁丢失时硬件会将其清零。软件可以读取它来判断当前是主模式还是从模式。TX (Bit 4)传输方向选择位。这是最易出错的地方之一。在主模式下发起传输前软件必须根据本次操作是“写”还是“读”来正确设置该位写1读0。在从模式下当IIC1S.IAAS位因地址匹配被置1后软件需要读取IIC1S.SRW位得知主设备期望的传输方向然后据此设置TX位。TXAK (Bit 3)发送应答使能位。当本设备作为接收方时此位决定在收到一个字节后是否在第9个时钟周期发出ACK低电平。0发出ACK1发出NACK高电平。通常在连续读取多个字节时最后一个字节之前都应置0发ACK读取最后一个字节后应置1发NACK通知发送方停止发送。RSTA (Bit 2)重复起始位。这是一个只写位写1有效。当本设备是当前总线主设备时向此位写1可以产生一个重复起始Repeated START信号。注意必须在合适的时机如前一个字节传输完成且总线仍由本设备控制操作否则可能导致仲裁丢失。3.3 状态寄存器IIC1S总线的“晴雨表”IIC1S寄存器反映了IIC总线和模块的实时状态是调试时最重要的观察窗口。TCF (Bit 7)传输完成标志。当一个字节8位数据1位ACK传输完成时此位由硬件置1。清除方法有讲究在接收模式下通过读取IIC1D寄存器来清除在发送模式下通过写入IIC1D寄存器来清除。通常在中断服务程序中需要先检查TCF确认是一个字节传输完成中断再进行后续操作。IAAS (Bit 6)被寻址为从设备标志。当接收到的呼叫地址与本机IIC1A中设置的地址匹配或使能了通用呼叫且收到通用呼叫地址0x00时此位置1。此时CPU应进入中断读取SRW位并相应设置IIC1C1.TX位然后写入IIC1C1寄存器任何值来清除此位。这是一个容易遗漏的操作。BUSY (Bit 5)总线忙标志。此位由硬件根据总线上的START和STOP信号自动设置和清除。1表示总线正被占用有通信在进行0表示总线空闲。在尝试发起通信前务必检查此位是否为0否则可能破坏正在进行的通信。ARBL (Bit 4)仲裁丢失标志。当多主竞争总线失败时此位置1。此位必须由软件写1来清除。发生仲裁丢失后模块会自动从主模式切换到从模式软件需要处理这个错误通常包括清除ARBL位、可能的重试逻辑等。SRW (Bit 2)从设备读/写标志。当IAAS被置1时此位表示主设备在地址帧中发出的R/W#位。0表示主设备要写数据给本从设备本设备应设置为接收模式TX01表示主设备要从本从设备读数据本设备应设置为发送模式TX1。IICIF (Bit 1)中断标志位。当TCF、IAAS或ARBL中任一事件发生且IICIE使能时此位置1。此位必须由软件写1来清除。在中断服务程序末尾必须执行此操作否则会持续产生中断。RXAK (Bit 0)接收应答位。当本设备作为发送方发送完一个字节后此位反映了接收方在第9个时钟周期返回的应答信号。0表示收到ACK成功1表示收到NACK失败。发送方应根据此位决定是继续发送、重发还是终止传输。3.4 数据I/O寄存器IIC1D与地址寄存器IIC1A, IIC1C2IIC1D数据寄存器。读写这个寄存器是启动数据传输的“扳机”。在主发送模式下写入IIC1D会启动一次数据发送如果是START后的第一次写入发送的是地址帧。在主接收模式下读取IIC1D会启动一次数据接收。手册中有一个非常重要的警告当从主接收模式切换出来时例如读完最后一个字节准备发STOP必须先切换模式如清零MST位或改变TX方向再读取IIC1D。否则读取操作可能会无意中启动一次新的接收周期。IIC1A从设备地址寄存器。在7位地址模式下AD[7:1]存放本设备的7位从地址。注意最低位bit 0是保留的应写0。IIC1C2控制寄存器2。GCAEN位用于使能/禁用通用呼叫地址0x00响应。ADEXT位用于选择7位0或10位1地址模式。在10位地址模式下AD[10:8]存放10位从地址的最高三位。4. 从零开始MC13234/37 IIC主机驱动实现理论说了一堆现在我们动手写一个针对MC13234/37的IIC主机驱动。我们将采用轮询非中断方式因为它更直观适合理解流程。在实际高负载系统中中断或DMA方式效率更高。4.1 初始化配置首先我们需要配置引脚功能。假设SDA和SCL分别对应MCU的PTB6和PTB7我们需要将这两个引脚配置为开漏输出并使能上拉如果MCU内部有可配置上拉电阻。// 引脚初始化示例 (需根据具体MCU的GPIO寄存器调整) void IIC_Pin_Init(void) { // 1. 将PTB6, PTB7设置为GPIO功能 PTBDD ~((16) | (17)); // 先设为输入避免冲突 // 假设存在寄存器将引脚功能设为IIC具体名称查阅手册 // 这里以GPIO为例实际需配置为IIC复用功能 // 2. 配置为上拉开漏模式如果MCU支持 // 通常需要使能内部上拉并设置输出为开漏 PTBPE | (16) | (17); // 使能内部上拉电阻 // 对于开漏需设置输出寄存器为1方向为输出时输出1即为高阻态靠上拉拉高 PTBD | (16) | (17); // 输出高电平释放总线 PTBDD | (16) | (17); // 设置为输出方向开漏模式下输出0驱动低输出1为高阻 }接下来是IIC模块本身的初始化#define IIC_BUS_CLK_HZ 16000000UL // 假设总线时钟16MHz #define IIC_TARGET_BAUD 100000UL // 目标波特率100kbps void IIC_Init(void) { // 1. 先禁用IIC模块 IIC1C1 ~(17); // IICEN 0 // 2. 配置IIC1F频率寄存器目标100kbps // 根据手册表13-4选择 MULT2 (01b), ICR0x14 // MULT在bit7:6, ICR在bit5:0 IIC1F (0x01 6) | 0x14; // MULT01b, ICR0x14 // 3. 配置本机地址如果作为从机需要 IIC1A 0x50; // 假设7位从机地址为0x50 (左移一位后是0xA0这里存0x50) // 4. 配置控制寄存器27位地址禁用通用呼叫 IIC1C2 0x00; // GCAEN0, ADEXT0 // 5. 配置控制寄存器1禁用中断初始为从机模式发送ACK IIC1C1 0x00; // IICEN0, IICIE0, MST0, TX0, TXAK0 // 6. 清除所有状态标志通过读IIC1D清TCF写IIC1C1清IAAS写1清ARBL和IICIF (void)IIC1D; // 读IIC1D清可能的TCF IIC1C1 | 0x00; // 写IIC1C1任何值清可能的IAAS IIC1S | (14) | (11); // 写1清ARBL和IICIF位 // 7. 最后使能IIC模块 IIC1C1 | (17); // IICEN 1 // 等待总线空闲 while(IIC1S (15)); // 等待BUSY位为0 }4.2 主机发送流程实现下面是一个向从设备地址slave_addr的指定寄存器reg_addr写入一个数据data的函数。uint8_t IIC_Master_Write_Byte(uint8_t slave_addr, uint8_t reg_addr, uint8_t data) { uint8_t status 0; // 返回状态0成功非0错误 // 步骤1等待总线空闲 while(IIC1S (15)); // 等待BUSY位为0 // 步骤2产生START条件通过设置MST和TX并写入地址到IIC1D IIC1C1 | (15) | (14); // 置MST1主模式TX1发送模式 // 注意设置MST1且总线空闲时硬件会自动产生START信号 // 步骤3发送从机地址写方向 IIC1D (slave_addr 1) | 0x00; // 左移一位最低位R/W#0表示写 // 等待字节传输完成 while(!(IIC1S (17))); // 等待TCF置位 // 检查是否收到ACK if(IIC1S (10)) { // 检查RXAK位1表示NACK status 1; // 地址无应答错误 goto error; } // 清除TCF标志通过写IIC1D但此时是发送模式我们下一步才写数据 // 实际上在发送模式下TCF会在下一次写入IIC1D时被清除。这里我们暂时不管。 // 步骤4发送寄存器地址 IIC1D reg_addr; while(!(IIC1S (17))); if(IIC1S (10)) { status 2; // 寄存器地址无应答错误 goto error; } // 步骤5发送数据 IIC1D data; while(!(IIC1S (17))); if(IIC1S (10)) { status 3; // 数据无应答错误 goto error; } // 步骤6产生STOP条件 IIC1C1 ~(15); // 清零MST位硬件会产生STOP信号 // 等待STOP完成BUSY变0 while(IIC1S (15)); return 0; // 成功 error: // 发生错误产生STOP信号释放总线 IIC1C1 ~(15); // 清除可能的错误标志 IIC1S | (14) | (11); // 清ARBL和IICIF while(IIC1S (15)); // 等待总线空闲 return status; }4.3 主机接收流程实现下面是一个从从设备地址slave_addr的指定寄存器reg_addr读取一个数据的函数。这需要先写入寄存器地址然后发送重复起始信号再发起读操作。uint8_t IIC_Master_Read_Byte(uint8_t slave_addr, uint8_t reg_addr, uint8_t *pdata) { uint8_t status 0; // 第一部分发送寄存器地址写操作 // 步骤1等待总线空闲 while(IIC1S (15)); // 步骤2产生START发送从机地址写 IIC1C1 | (15) | (14); // MST1, TX1 IIC1D (slave_addr 1) | 0x00; // R/W# 0 while(!(IIC1S (17))); if(IIC1S (10)) { status 1; goto error; } // 步骤3发送要读取的寄存器地址 IIC1D reg_addr; while(!(IIC1S (17))); if(IIC1S (10)) { status 2; goto error; } // 第二部分发送重复起始切换为读操作 // 步骤4发送重复起始信号 IIC1C1 | (12); // 置RSTA位为1产生重复START // 注意此时MST仍为1TX仍为1。产生重复START后硬件会保持主模式。 // 步骤5发送从机地址读方向 IIC1D (slave_addr 1) | 0x01; // R/W# 1表示读 while(!(IIC1S (17))); if(IIC1S (10)) { status 3; goto error; } // 步骤6切换为主接收模式并准备接收数据发NACK因为只读一个字节 IIC1C1 ~(14); // TX0切换为接收模式 IIC1C1 | (13); // TXAK1接收完下一个字节后发NACK // 步骤7启动接收读IIC1D寄存器 (void)IIC1D; // 虚读启动接收过程 while(!(IIC1S (17))); // 等待接收完成 // 步骤8读取接收到的数据 *pdata IIC1D; // 读取数据同时会清除TCF标志 // 步骤9产生STOP条件 IIC1C1 ~(15); // 清零MST产生STOP // 重要在产生STOP前先切换模式我们已经做了TX0再读数据上一步已读。 while(IIC1S (15)); // 等待总线空闲 return 0; error: IIC1C1 ~(15); IIC1S | (14) | (11); while(IIC1S (15)); return status; }5. 调试实战常见问题排查与解决技巧即使按照手册和示例代码配置IIC通信仍可能出问题。以下是基于MC13234/37的典型问题排查清单。5.1 通信完全无响应示波器看不到波形问题现象程序运行后用示波器测量SDA和SCL线始终为高电平没有任何波形。排查步骤检查硬件连接确保SDA和SCL线正确连接上拉电阻通常4.7kΩ已焊接且连接到正确的VDD。用万用表测量两条线对地电压应为VDD如3.3V。检查引脚复用确认MCU的SDA和SCL引脚是否已正确配置为IIC功能而非普通的GPIO。MC13234/37的IIC功能通常有固定的引脚需查阅数据手册的引脚复用表。检查模块使能确认IIC1C1.IICEN位是否已置1。这是最容易被忽略的一步。检查总线忙状态在发起START前程序是否在等待IIC1S.BUSY位为0如果总线被意外锁死例如从设备拉低了SCLBUSY会直为1。可以尝试短暂断电重启整个系统。检查START生成确认在设置MST1和TX1后是否立即写入了地址到IIC1D写入IIC1D是触发地址帧发送的动作。如果只设置了控制位而没有写数据START信号可能不会产生。5.2 能收到ACK但后续数据错误或丢失问题现象示波器显示START、地址帧和ACK都正常但发送或接收的数据字节错误或者从设备没有回应后续的数据ACK。排查步骤检查时序用示波器测量SCL频率计算是否与配置的波特率一致如100kHz对应周期10us。检查SDA数据变化是否发生在SCL低电平期间并在SCL高电平期间保持稳定。MC13234/37的保持时间由IIC1F寄存器配置如果从设备要求严格的建立/保持时间可能需要调整ICR值。检查TX方向位在发送地址帧后如果要发送数据IIC1C1.TX位必须保持为1发送模式。如果要接收数据必须在发送读地址后先将TX位清零接收模式再去读取IIC1D来启动接收。顺序错误是导致接收失败的常见原因。检查ACK处理在发送模式下每个字节发送后是否检查了IIC1S.RXAK位如果收到NACK应终止传输。在接收多个字节时是否在接收最后一个字节前将IIC1C1.TXAK置1以发送NACK通知从设备结束发送检查从设备状态确认从设备如传感器、EEPROM是否已上电、初始化完成。有些设备在写入后需要几毫秒的写入周期Write Cycle Time在此期间不会响应IIC命令。5.3 多主系统中仲裁丢失频繁问题现象在多主系统中本设备经常无法成功发送数据IIC1S.ARBL位频繁置1。排查步骤检查总线竞争逻辑确保你的主设备在发起传输前都正确检查了BUSY位。两个主设备几乎同时检测到总线空闲并启动传输是仲裁丢失的直接原因。可以引入随机延时来减少碰撞概率。处理仲裁丢失在代码中每次传输后特别是在错误处理中必须检查并清除ARBL位。仲裁丢失后模块会自动转为从模式你的主设备驱动需要能够从这种状态恢复重新尝试传输。分析地址优先级仲裁发生在地址和数据阶段。如果你的设备地址值较大二进制高位更多是1在仲裁中天然处于劣势。如果可能为高优先级的主设备分配数值较小的从地址如果需要寻址的话实际上主设备在发送阶段用从设备地址自身地址不参与仲裁这里需要澄清在多主通信中仲裁发生在所有主设备同时发送的位流上包括地址和数据。地址值小的主设备在发送地址时如果与其他主设备冲突因其发送的0更多更容易赢得仲裁。但主设备本身没有“主地址”它发送的是目标从设备的地址。5.4 中断驱动下的异常行为问题现象使用中断方式驱动IIC时程序偶尔卡死或数据混乱。排查步骤清除中断标志在中断服务程序ISR中是否在处理完事件后写1清除了IIC1S.IICIF位忘记清除会导致中断持续触发卡死系统。区分中断源ISR中是否根据TCF、IAAS、ARBL等状态位正确区分了“字节传输完成”、“被寻址”、“仲裁丢失”等不同事件处理逻辑是否针对每种事件都正确临界区保护主程序如准备发送数据和ISR处理发送完成是否共享了关键变量或状态机需要使用关中断或信号量进行保护。ISR效率IIC中断可能频率较高每个字节一次ISR应尽可能短小快出只做必要的状态更新和标志操作将数据处理等耗时任务放到主循环中。调试终极武器示波器与逻辑分析仪没有比这更有效的工具了。抓取一次完整的通信波形对照IIC协议时序图逐位分析START、地址、ACK、数据、STOP是否完全符合规范。逻辑分析仪配合IIC解码功能能直观地显示地址、数据值、ACK/NACK极大提升调试效率。很多时候问题就藏在某个微小的时序违规里。