公司动态
深入解析I2C总线协议:从时钟同步到中断机制与寄存器配置
1. 项目概述在嵌入式系统开发中I2C总线协议因其简洁的两线制SDA数据线和SCL时钟线和灵活的主从架构成为了连接微控制器与各类传感器、存储器、IO扩展芯片等外设的“血管”。然而很多开发者仅仅停留在调用现成库函数进行读写操作的层面对于I2C模块内部如何精准地协调时钟、高效地处理数据流以及如何通过寄存器精细控制其行为往往知其然而不知其所以然。这种“黑盒”式的使用在面对复杂的多主竞争、高速率通信或苛刻的时序要求时往往会导致通信不稳定、调试困难等问题。今天我们就以德州仪器TIDSP系列芯片中常见的I2C模块参考文档SPRU175D为蓝本进行一次“庖丁解牛”式的深度解析。我们将不满足于简单的API调用而是深入到时钟同步、中断机制和寄存器配置这三个核心层面。你将理解在多个主设备争夺总线时SCL时钟线如何像一场默契的“拔河比赛”一样实现同步你将掌握如何通过中断而非低效的轮询让CPU从繁重的字节搬运工作中解放出来你更将学会如何直接“对话”寄存器实现对I2C模块每一个细微行为的精准操控。无论你是正在调试一个I2C设备驱动还是希望优化现有通信代码的效率这篇文章都将为你提供从原理到实践的完整路线图。2. I2C模块核心机制深度剖析要驾驭I2C模块绝不能只把它看作一个简单的数据收发器。它内部实则是一个由状态机、时钟发生器、仲裁逻辑和中断系统构成的精密协同体。理解其核心工作机制是进行高效、稳定编程的基础。2.1 时钟同步多主系统中的“节奏大师”在单一主设备场景下SCL时钟由主设备独家产生一切井然有序。但I2C协议的魅力在于支持多主Multi-Master架构允许多个微控制器共享同一总线。当两个或更多主设备几乎同时发起传输时冲突便不可避免。此时时钟同步机制就是确保总线不被“扯断”、数据能被正确比较的基石。其核心原理依赖于SCL线的“线与”Wired-AND物理特性。这意味着SCL线被上拉电阻拉高任何设备都可以通过将其驱动的晶体管导通来将线路拉低输出‘0’只有当所有设备都释放总线输出高阻态等效‘1’时SCL线才会被上拉电阻拉回高电平。同步过程拆解竞争开始假设主设备A和B同时开始传输并各自驱动自己的SCL时钟发生器。低电平优先一旦某个设备比如设备A的时钟发生器输出由高变低它将SCL线拉低。由于“线与”特性这个低电平会立刻覆盖其他设备试图维持的高电平SCL总线实际变为低电平。强制同步设备B检测到SCL线被外部拉低尽管自己内部时钟可能还在高电平周期它的硬件会立即中断当前的高电平周期强制进入低电平周期并与总线上的低电平同步。这就实现了所有主设备时钟低电平周期的起点对齐。高电平等待当设备A的低电平周期结束它会释放SCL线输出高阻态。但此时如果设备B的低电平周期尚未结束它仍在拉低SCL线。因此SCL线将保持低电平直到所有设备的低电平周期都结束。高电平开始当最后一个设备比如设备B结束其低电平周期并释放SCL线后所有设备检测到SCL线变为高电平它们才同时开始各自的高电平周期。最终效果同步后的SCL时钟其低电平周期长度由所有竞争设备中低电平周期最长的那个决定而高电平周期长度则由最先结束低电平并开始驱动高电平的设备决定实际上由于上拉电阻和总线电容高电平的上升沿会稍慢。这就好比一场合唱起唱低电平开始大家必须整齐划一而每个人唱完一个音符的时长低电平宽度必须等待最慢的那个人但切换到下一个音符高电平则可以由最快的人引领。实操心得理解时钟同步对于调试多主系统至关重要。如果你发现通信速度比预期慢特别是在有多个主设备或某些低速从设备如某些EEPROM在写入周期内会主动拉低SCL以等待写入完成称为“时钟延展”的系统中很可能是某个设备延长了SCL的低电平周期。此时需要检查每个设备的时钟配置和总线负载。2.2 中断机制让CPU“解放双手”的关键轮询Polling寄存器状态位是一种简单但低效的方式它会让CPU陷入忙等待浪费宝贵的计算资源。I2C模块提供了完善的中断驱动机制让数据传输可以异步进行。I2C模块内部可以产生多种中断请求但它们通过一个仲裁器Arbiter合并成一个单一的I2C中断信号输出给CPU。这种设计简化了CPU的中断向量管理但要求中断服务程序ISR必须能快速识别中断源。中断产生的双条件机制 每个中断事件都对应状态寄存器I2CSTR中的一个标志位Flag和中断使能寄存器I2CIER中的一个使能位Enable。中断产生的路径是事件发生 → 标志位置1 → 若对应使能位为1 → 中断请求发送至仲裁器 → 仲裁器根据优先级转发至CPU。核心中断类型解析中断请求标志位 (I2CSTR)使能位 (I2CIER)触发条件典型应用场景XRDYINTICXRDYICXRDY发送数据寄存器I2CDXR就绪可以写入下一字节数据。主/从发送模式下当前字节已从I2CDXR移入发送移位寄存器I2CXSR准备发送下一个字节。RRDYINTICRRDYICRRDY接收数据寄存器I2CDRR就绪可以读取已接收的字节。主/从接收模式下接收移位寄存器I2CRSR的数据已移入I2CDRRCPU可以安全读取。ARDYINTARDYARDY寄存器访问就绪仅主模式。先前编程的地址、数据、命令值已被使用。在主模式下配置完I2CMDR如设置STT、STP或写入I2CDXR后等待此中断确认操作已被模块接受可进行下一步配置。NACKINTNACKNACK作为主发送器未收到从机的应答NACK。从机地址错误、从机忙或通信故障时用于错误处理流程。ALINTALAL仲裁丢失仅主发送器。多主竞争时本机竞争失败需退回到从机模式并可能重试。中断服务程序ISR处理流程进入I2C中断服务例程。读取中断源寄存器I2CISR该寄存器的值指明了当前触发中断的最高优先级事件源如010b代表RRDYINT。这是一个关键操作因为读取I2CISR会自动清除I2CSTR中对应事件的标志位ARDY、ICRRDY、ICXRDY这三个位除外它们需要手动写1清除。根据I2CISR的值跳转到对应的处理子程序如数据发送、数据接收、错误处理等。在子程序中进行相应的操作如写入I2CDXR、读取I2CDRR、清除错误标志等。对于ARDY、ICRRDY、ICXRDY标志如果需要手动清除应向I2CSTR对应位写1。注意事项中断嵌套与优先级。I2C模块内部的中断仲裁器有其固定优先级通常AL和NACK优先级较高。但在CPU层面你需要配置好系统中断控制器如NVIC确保I2C中断能及时响应。同时ISR应尽可能短小精悍避免复杂操作。如果需要处理大量数据考虑在ISR中设置标志在主循环中处理或者使用DMA。2.3 复位与配置的黄金法则IRS位的奥秘在深入配置寄存器之前有一个必须严格遵守的“铁律”它关乎整个模块能否正常工作。核心原则绝大多数关键配置寄存器必须在I2C模块处于复位状态IRS 0时进行写入或修改。这包括预分频器寄存器I2CPSC决定模块的基础工作时钟。时钟高低电平分频寄存器I2CCLKL, I2CCLKH决定SCL时钟的频率和占空比。为什么因为I2C模块内部的状态机和时钟发生器在运行状态IRS1下是动态工作的。此时更改这些时序相关的底层配置可能会导致时钟信号出现毛刺、相位混乱直接引发通信失败或总线挂死。将IRS位清零相当于让模块内部逻辑“暂停”或“归零”此时配置寄存器是安全的。操作顺序务必牢记确保IRS 0模块复位。上电默认或软件写入I2CMDR的IRS位为0均可。配置I2CPSC、I2CCLKL、I2CCLKH等时序相关寄存器。配置I2COAR自身地址、I2CMDR中的工作模式位但先保持IRS0。将IRS 位置 1启动I2C模块。此时步骤2中的配置才会生效。在模块运行中IRS1可以安全地读写I2CDXR、I2CDRR、I2CSTR等数据与状态寄存器。复位方式软件复位向I2CMDR寄存器的IRS位写0。模块复位SDA和SCL引脚进入高阻态状态位恢复默认值。适用于动态关闭模块以省电或清除错误状态。硬件复位拉低DSP的RESET引脚。整个芯片复位所有I2C寄存器恢复默认值IRS0。上电后需重新配置。3. 寄存器配置详解与实战编程理解了核心机制后我们进入实战环节。寄存器是我们与I2C模块硬件对话的直接接口。我们将逐一剖析关键寄存器并给出具体的配置示例和计算公式。3.1 时钟配置从CPU频率到SCL波形I2C总线的通信速率标准模式100kbps快速模式400kbps高速模式3.4Mbps由SCL时钟决定。我们需要通过三个寄存器将CPU的主时钟CLK分频得到所需的SCL。配置流程与计算公式确定模块输入时钟Module Clock首先通过预分频器I2CPSC对CPU时钟进行初步分频。公式为模块时钟频率 CPU时钟频率 / (I2CPSC 1)I2CPSC的值必须大于等于1。其目的是将高速的CPU时钟降到一个适合I2C模块内部操作的中间频率。生成SCL时钟然后模块时钟再经过I2CCLKL和I2CCLKH两个分频器分别产生SCL的低电平周期和高电平周期。SCL时钟频率计算公式为SCL频率 模块时钟频率 / [(I2CCLKL d) (I2CCLKH d)]其中d是一个取决于具体芯片型号的固定延迟值通常为5-7需要查阅芯片数据手册Datasheet的I2C章节确认。例如某芯片d5。SCL的占空比由这两个寄存器决定低电平时间占比 (I2CCLKL d) / [(I2CCLKL d) (I2CCLKH d)]实战配置示例 假设CPU时钟为150 MHz目标SCL频率为400 kHz (快速模式)芯片的延迟因子 d 6且希望占空比接近50%。步骤1计算I2CPSC。模块时钟不宜过高或过低通常取10-20MHz范围。我们目标模块时钟为12MHz。I2CPSC (CPU时钟 / 模块时钟) - 1 (150MHz / 12MHz) - 1 11.5取整为11。则实际模块时钟 150MHz / (111) 12.5 MHz。步骤2计算I2CCLKL和I2CCLKH。总除数N 模块时钟 / SCL频率 12.5MHz / 400kHz 31.25由于N (I2CCLKL d) (I2CCLKH d)且希望两者相等占空比50%则(I2CCLKL d) ≈ (I2CCLKH d) ≈ N / 2 15.625减去延迟dI2CCLKL I2CCLKH 15.625 - 6 9.625取整为10。验证SCL频率 12.5MHz / [(106) (106)] 12.5MHz / 32 390.625 kHz接近目标值。注意事项实际频率的验证。由于取整和延迟因子的存在计算出的频率可能与理论值有偏差。在时序要求严格的系统中如与某些特定传感器通信务必使用逻辑分析仪或示波器测量实际的SCL频率和占空比并根据测量结果微调寄存器值。I2CCLKL和I2CCLKH的值必须至少为2。3.2 模式与数据寄存器控制通信流程I2C模式寄存器I2CMDR这是I2C模块的“大脑”控制着所有主要功能。MST位11主/从模式选择。1为主模式0为从模式。TRX位9发送/接收模式。1为发送器0为接收器。XA位14地址扩展。0为7位地址模式1为10位地址模式。RM位12重复模式。这是一个关键位RM0非重复模式配合内部数据计数器I2CCNT使用。当计数器减到0时模块会自动根据STP位的设置产生停止条件或重复起始条件。适用于传输数据量已知的场景。RM1重复模式不使用内部计数器。数据传输完全由软件通过检查状态位如ARDY或响应中断来控制。适用于数据量可变或需要复杂控制的场景。STT位13与STP位12起始/停止条件控制位。仅在主模式下有效。软件置位STT1产生起始条件置位STP1产生停止条件。硬件会在操作完成后自动清除这些位。IRS位15模块复位/使能位。如前所述0复位1使能。数据与地址寄存器I2COAR自身地址寄存器。当模块作为从机时用它来响应主机的寻址。I2CSAR从机地址寄存器。当模块作为主机时存放目标从机的地址。I2CDXR数据发送寄存器。CPU将待发送的数据写入此寄存器。I2CDRR数据接收寄存器。CPU从此寄存器读取接收到的数据。I2CCNT数据计数寄存器非重复模式下使用。初始化时应写入待传输的数据字节数。3.3 状态寄存器I2CSTR系统的“眼睛”I2CSTR寄存器是我们监控总线状态和模块内部状态的核心。除了之前提到的中断标志位ICXRDY, ICRRDY, ARDY, NACK, AL还有几个关键状态位BB位12总线忙标志。1表示总线正忙检测到起始条件后0表示总线空闲检测到停止条件后。在主机发起传输前必须检查BB是否为0。RSFULL位11接收移位寄存器满。当I2CRSR已满但I2CDRR中旧数据还未被读取时此位置1表示发生溢出。新数据会覆盖I2CRSR中的旧数据导致丢失。这是一个错误状态需要在ISR中处理。XSMT位10发送移位寄存器空。当I2CXSR已空但I2CDXR中还没有新数据写入时此位置0表示发生下溢。发送器会重复发送最后一个字节或发送无意义数据。这同样是一个错误状态。AAS位9被寻址为从机。当模块识别到自己的从机地址或全零的广播地址时此位置1。在从机模式下可用此位判断自己是否被主机呼叫。4. 编程实战三种数据流控制模式详解理论最终要服务于实践。SPRU175D手册提供了清晰的流程图我们将其转化为更易理解的代码逻辑和操作步骤。数据流控制主要有三种模式位轮询、中断和EDMA事件。4.1 模式一位轮询Bit Polling模式这是最简单直接的方式CPU不断查询I2CSTR中的状态位如ICXRDY, ICRRDY, ARDY来推进传输。优点是逻辑简单无需中断配置缺点是CPU利用率低在等待期间被完全占用。实战案例主机发送器非重复模式RM0这种模式适用于发送固定长度数据的场景例如向EEPROM写入一个已知长度的配置块。// 假设I2C模块已初始化时钟已配置自身地址(I2COAR)已设置目标从机地址已写入I2CSAR。 // 要发送的数据存放在数组 txBuffer[] 中长度为 dataLength。 // 1. 检查总线是否空闲 while (I2CSTR BB_MASK); // 等待BB位为0 // 2. 配置I2CMDR主机模式(MST1)发送器(TRX1)非重复模式(RM0)产生起始条件(STT1)最后产生停止条件(STP1) // 同时将模块使能(IRS1)。假设使用7位地址(XA0)。 // 寄存器值需根据位域计算例如0x2E20 (二进制: 0010 1110 0010 0000) I2CMDR 0x2E20; // 此值包含了 IRS1, MST1, TRX1, RM0, STT1, STP1 // 3. 写入数据长度到计数器非重复模式必须 I2CCNT dataLength; // 4. 等待寄存器访问就绪ARDY1这表示模块已接受初始配置并开始工作 while (!(I2CSTR ARDY_MASK)); // 5. 主循环发送数据 for (int i 0; i dataLength; i) { // 等待发送数据寄存器就绪ICXRDY1 while (!(I2CSTR ICXRDY_MASK)); // 检查是否收到NACK从机无应答 if (I2CSTR NACK_MASK) { // 处理错误清除NACK标志重试或退出 I2CSTR | NACK_MASK; // 写1清除NACK位 handleError(); break; } // 写入待发送数据 I2CDXR txBuffer[i]; // 每发送完一个字节等待寄存器访问就绪ARDY1以便进行下一步对于最后一个字节是等待STP条件生成 while (!(I2CSTR ARDY_MASK)); } // 6. 传输完成等待停止条件生成STP位由硬件在完成后自动清零但ARDY就绪可间接指示 // 通常等待ARDY就绪后传输即告完成。实操心得在轮询ARDY位时特别是在非重复模式RM0下需要理解其行为。对于中间的数据字节ARDY在内部计数器递减后置位对于最后一个字节当STP1时ARDY不会因计数器到0而置位而是等待停止条件完成后才置位。因此用等待ARDY作为传输完成的标志是可靠的。4.2 模式二中断Interrupt模式中断模式将CPU从轮询中解放出来。当特定事件如数据就绪、寄存器就绪、错误发生时硬件触发中断CPU跳转到ISR处理。实战案例主机接收器重复模式RM1数据量可变这种模式适用于读取一个传感器其数据长度可能由之前发送的命令决定或者需要读取直到某个特定终止符。// 全局变量 volatile uint8_t rxBuffer[256]; volatile int rxIndex 0; volatile bool receptionComplete false; volatile bool errorFlag false; // 主程序初始化部分 void I2C_Master_Receive_VariableLength(uint8_t slaveAddr, uint8_t regAddr) { // 1. 配置中断使能使能接收就绪(RRDY)、寄存器就绪(ARDY)、无应答(NACK)和仲裁丢失(AL)中断 I2CIER ICRRDY | ARDY | NACK | AL; // 例如0x0F // 2. 配置系统中断控制器使能I2C中断此处为伪代码依赖具体MCU enable_I2C_interrupt(); // 3. 检查总线空闲 while (I2CSTR BB_MASK); // 4. 配置I2CMDR主机(MST1)接收器(TRX0)重复模式(RM1)产生起始(STT1)最后产生停止(STP1) // 注意重复模式下STP位的行为可能不同通常需要软件在收到足够数据后设置STP。 I2CMDR 0x24A0; // 示例值需根据位域调整 // 5. 写入从机地址假设先发送一个寄存器地址 I2CSAR slaveAddr; // 写入要读取的寄存器地址作为第一个发送字节 while (!(I2CSTR ICXRDY_MASK)); I2CDXR regAddr; // 6. 等待第一个ARDY中断表示地址和寄存器地址已发送可以切换为接收模式 // 后续操作将在中断服务程序中完成 } // I2C中断服务程序 (ISR) void I2C_ISR(void) { uint16_t intSource I2CISR; // 读取中断源并自动清除大部分标志 switch(intSource) { case SOURCE_ARDY: // 寄存器访问就绪 // 在发送完从机地址和寄存器地址后ARDY中断触发 // 此时需要发送一个重复起始条件Repeated Start来切换为读操作 // 重新配置I2CMDR保持主机模式切换为接收器设置STT1重复起始 I2CMDR 0x24A0; // 重新发送起始条件地址R/W位现在应为读(1) // 注意需要根据实际情况计算I2CMDR值这里仅为示意 break; case SOURCE_RRDY: // 接收数据就绪 rxBuffer[rxIndex] I2CDRR; // 读取数据 // 判断是否接收完毕例如收到特定结束符或达到最大长度 if (rxIndex sizeof(rxBuffer) || rxBuffer[rxIndex-1] STOP_CHAR) { // 产生停止条件 I2CMDR | STP_MASK; // 设置STP位 receptionComplete true; // 禁用接收中断避免后续数据干扰 I2CIER ~ICRRDY; } break; case SOURCE_NACK: // 无应答 I2CSTR | NACK_MASK; // 手动清除NACK标志 errorFlag true; // 产生停止条件并终止传输 I2CMDR | STP_MASK; break; case SOURCE_AL: // 仲裁丢失 I2CSTR | AL_MASK; // 手动清除AL标志 errorFlag true; // 仲裁丢失后模块自动转为从机需根据应用决定是否重试 break; } }注意事项中断标志的清除。务必仔细阅读数据手册。读取I2CISR会清除大部分中断源标志但ARDY、ICRRDY、ICXRDY这三个标志需要向I2CSTR对应位写1来清除。在ISR中如果处理不当会导致中断不断重复触发陷入死循环。4.3 模式三EDMA事件模式这是最高效的模式适用于大批量、高速的数据传输。I2C模块可以产生两个EDMA增强型直接内存访问事件XEVT发送事件和REVT接收事件。当I2CDXR数据移入I2CXSR时触发XEVTEDMA控制器自动将下一数据从内存写入I2CDXR当数据从I2CRSR移入I2CDRR时触发REVTEDMA控制器自动将数据从I2CDRR读入内存。CPU几乎不参与数据传输过程。配置要点初始化EDMA配置EDMA通道的源地址、目的地址、传输数量、地址增量模式等。对于发送源地址是内存数据区目的地址是I2CDXR对于接收则相反。配置I2C中断通常仍需使能ARDY、NACK、AL等中断用于控制传输的启动、停止和错误处理。数据流本身由EDMA管理。关联事件将EDMA通道的同步事件设置为I2C的XEVT或REVT。启动传输软件配置好I2C并启动传输后数据搬运就由EDMA全权负责直到传输完成触发EDMA传输完成中断。这种模式将CPU彻底解放使其能够处理其他任务特别适合需要连续采集或发送大量数据的应用如音频流、图像传感器数据读取等。5. 常见问题排查与调试技巧实录即使理解了所有原理和步骤在实际调试中依然会遇到各种问题。以下是我在多年项目中总结的常见“坑点”和解决方法。5.1 通信完全无响应SCL/SDA始终为高检查清单物理连接确保SDA和SCL线已正确连接并且通过上拉电阻通常4.7kΩ拉至电源电压。用万用表测量总线电压空闲时应为高电平VCC。模块使能确认I2CMDR寄存器中的IRS位是否已设置为1。这是最容易被忽略的一步IRS0时模块处于复位状态引脚为高阻态。引脚复用确认MCU的I2C引脚功能是否已正确映射到GPIO的复用功能Alternate Function。很多MCU的引脚默认是普通GPIO。时钟配置确认I2CPSC、I2CCLKL、I2CCLKH寄存器是否已在IRS0时正确配置。使用示波器测量SCL引脚是否有波形输出。5.2 能发送起始条件但地址无应答NACK排查思路从机地址确认写入I2CSAR的从机地址是否正确7位地址左移一位后最低位是R/W位。例如设备地址0x487位作为写操作时I2CSAR应写入0x48 1 0x90。从机电源与就绪确保从设备已上电并已完成初始化有些EEPROM上电后需要几毫秒的等待时间。总线冲突是否有其他设备包括另一个MCU也在驱动总线用逻辑分析仪同时抓取SDA和SCL看波形是否干净有无毛刺或中间电平。时序问题SCL频率是否在从设备支持的范围内过快可能导致从设备来不及响应。尝试降低SCL频率增大I2CCLKL/H值。5.3 能收到应答但数据发送/接收错误典型症状数据位错误、CRC校验失败、读取的数据全为0xFF或0x00。深度排查逻辑分析仪是王道连接逻辑分析仪解码I2C协议。对比主机发送的数据波形和从机响应的波形。检查每个数据位、ACK位的时序和电平。检查ICXRDY/ICRRDY和ARDY的时序在轮询模式下是否在正确的时机读写I2CDXR和I2CDRR必须在ICXRDY1时写入I2CDXR在ICRRDY1时读取I2CDRR。操作太快或太慢都会导致数据覆盖或丢失。溢出Overrun与下溢Underflow检查I2CSTR中的RSFULL和XSMT位。如果RSFULL1说明接收数据太快CPU没来得及读走新数据覆盖了旧数据。如果XSMT0说明发送数据太慢发送寄存器空了。这通常是由于中断响应太慢或轮询间隔太长或者CPU被更高优先级任务阻塞。中断服务程序效率在中断模式下ISR是否执行时间过长是否在ISR中进行了复杂的计算或函数调用这可能导致丢失后续的中断。优化ISR仅做最必要的操作如读写数据寄存器、设置标志繁重的处理放到主循环中。5.4 多主系统中仲裁丢失AL现象通信随机失败I2CSTR中的AL位被置1。分析与解决理解仲裁仲裁发生在SDA数据线当多个主机同时发送起始条件后随后的地址和数据位会进行“线与”比较。哪个主机发送了高电平‘1’但检测到总线为低电平‘0’它就失去仲裁关闭其输出驱动器。软件策略在AL中断服务程序中应清除AL标志并执行退避算法。简单的做法是等待一个随机时间后重试。更复杂的系统可能需要设计总线优先级。硬件设计确保总线电容不要过大过长的走线或过多的设备负载会导致信号边沿变缓增加仲裁的不确定性。遵循I2C总线规范设计PCB布局。5.5 调试工具箱推荐逻辑分析仪如Saleae Logic系列配合I2C解码软件是调试I2C通信的首选利器。可以直观看到起始、停止、地址、数据、ACK/NACK每一位的波形和时间戳。示波器用于测量SCL频率、占空比、信号上升/下降时间检查信号完整性过冲、振铃。软件调试器结合IDE如Code Composer Studio, IAR, Keil的实时寄存器查看和内存查看功能在断点处检查I2C所有寄存器的值与预期状态对比。“最笨”但有效的方法——模拟IO在极度受限的环境下可以用两个GPIO模拟SDA和SCL编写一个最简单的I2C主机程序验证从设备本身是否工作正常从而排除硬件故障。