I2C通信协议深度解析:从基础原理到UNICOMM-I2C模块实战应用 1. I2C通信核心从协议基础到模式选择I2CInter-Integrated Circuit总线协议对于嵌入式开发者而言既是老朋友也常常是新挑战。它那看似简单的两根线SDA数据线和SCL时钟线背后隐藏着一套完整的通信规则和状态机。我接触过不少项目从简单的温湿度传感器读取到复杂的多主设备音频编解码器通信I2C的稳定性和效率很大程度上取决于你对控制器Controller旧称Master和目标Target旧称Slave两种模式的理解深度以及如何巧妙地运用中断和FIFO来解放CPU。简单来说你可以把I2C总线想象成一条双向单车道SDA交通信号灯SCL由当前路权的“交警”Controller控制。Controller发起通话喊出目标地址Target Address对应的Target应答后双方开始传输数据。协议本身规定了起止信号、应答机制和时钟同步但具体到芯片内部的实现比如如何高效地搬移数据、如何及时响应事件就是各厂商IP核和驱动程序大显身手的地方了。TI MSPM0系列微控制器集成的UNICOMM-I2C模块提供了一个相当现代和灵活的硬件实现特别是其丰富的中断源和可配置的FIFO让DMA配合中断驱动编程成为提升系统性能的利器。在实际项目中选择Controller模式还是Target模式取决于你的设备在总线上的角色。Controller是主动方负责发起和终止传输、产生时钟信号Target是被动方监听地址并响应Controller的读写请求。很多MCU的I2C外设可以动态切换这两种模式以适应多主总线架构。理解这两种模式下的寄存器配置流程和中断处理逻辑是写出稳定高效I2C驱动代码的前提。接下来我们就深入UNICOMM-I2C模块的内部看看它是如何运作的。2. 控制器Controller模式详解配置与发送流程作为总线的主导者Controller模式下的配置需要格外细致任何一个寄存器位的疏忽都可能导致通信失败。UNICOMM-I2C模块的控制器功能主要通过几个核心寄存器来配置控制寄存器CTR、目标地址寄存器TA、状态寄存器SR以及中断FIFO级别选择寄存器IFLS。2.1 核心寄存器配置解析启动一次控制器发送事务远不止是往数据寄存器里填个数那么简单。它是一系列精心编排的寄存器操作。首先你需要通过TA寄存器告诉模块“你要和谁说话以及是读还是写”。TA.ADDR字段填入7位或10位目标设备地址TA.DIR位则设置方向0表示控制器发送写1表示控制器接收读。这里有个关键点地址需要左移一位最低位在I2C协议中代表读写位但硬件通常会帮你处理你只需填入地址值DIR位会决定最终发出的读写位。接下来是CTR寄存器它是事务的“总指挥”。CTR.BLEN字段在高级Advanced实例中用于设置本次事务传输的字节数n。这对于批量传输至关重要设置好后模块会在传输完n个字节后自动产生TXDONE或RXDONE中断而不是每字节中断一次大大减轻了CPU负担。对于基础Basic实例此长度固定为1意味着每传输一个字节就会触发一次完成中断编程逻辑需要相应调整。CTR.START和CTR.STOP位控制是否生成起始和停止条件。CTR.FRM_START位是“发车按钮”在所有参数地址、方向、长度、起停控制设置好后将其置1模块才会真正启动总线事务。注意在设置CTR.FRM_START1启动事务之前务必确认SR.BUSY位为0模块空闲并且在多控制器系统中还需确认SR.BUSBUSY位为0总线空闲。否则可能会破坏总线上的现有通信。2.2 控制器发送模式Transmitter Mode的两种中断策略根据输入材料中的流程图Figure 26-18, 26-19控制器发送数据主要有两种编程模型基于TXDONE的“每事务中断”模型和基于TXTRG的“FIFO中断”模型。选择哪种取决于你的数据块大小和对实时性的要求。基于TXDONE的模型更简单直接适合小数据量或对传输过程需要严格控制的场景。其流程是配置好TA和CTR后将数据写入TXDATA寄存器或FIFO然后启动事务。模块会发送起始条件、地址、数据最后根据CTR.STOP决定是否发送停止条件。当CTR.BLEN设定的所有字节都发送完毕会触发TXDONE中断。在此期间如果TX FIFO空了SR.TXFE1还会触发TXEMPTY中断提示你可以写入下一个数据——但这在BLEN大于1且一次性写入所有数据时可能用不到。这种模式下CPU需要等待每次传输完成吞吐量较低。基于TXTRG的模型则旨在最大化吞吐量是DMA的理想搭档。它的核心思想是利用FIFO。你首先通过IFLS.TXIFSEL设置一个FIFO触发水平例如“FIFO半空”或“FIFO剩余空间大于X字节”。当TX FIFO中的数据量低于这个阈值时就会触发TXTRG中断或DMA请求。在中断服务程序ISR中你只需检查剩余待发送数据并持续填充FIFO直到所有数据发送完毕最后由TXDONE中断通知事务完成。这样数据搬运从内存到FIFO的工作可以在后台由DMA完成或者由CPU在TXTRG中断中快速批量填充从而在大部分时间里避免了等待让总线保持高速连续传输。我个人的经验是对于超过8个字节的连续发送强烈建议使用TXTRGFIFODMA的方式。你需要仔细计算FIFO深度和触发水平避免触发过于频繁增加中断开销或触发太晚导致FIFO下溢总线停顿。一个常见的技巧是将TXTRG触发水平设置为“FIFO半空”这样每次中断可以填充一半FIFO的数据在填充期间另一半数据正在被发送形成了流水线操作。2.3 控制器接收模式与ACK/NACK处理控制器接收模式TA.DIR1的配置与发送类似但关注点在于接收数据的处理和应答控制。CTR.ACK和CTR.ACKOEN位在这里扮演重要角色。CTR.ACK决定了在接收事务的最后一个字节后控制器是否自动发送应答ACK。通常为了继续读取除了最后一个字节前面所有字节都应回复ACK最后一个字节回复NACK以示意Target停止发送。CTR.ACKOENACK覆盖使能位为1时硬件会在接收完BLEN指定的字节数后触发RXDONE中断并拉低SCL时钟拉伸等待软件决定是发送ACK还是NACK通过CTR.ACK值这给了软件在接收多字节数据流时动态控制的能力。接收同样有RXDONE和RXTRG两种模式。RXDONE模式在每接收完一个数据块BLEN长度后中断适合需要即时处理每个数据包的场景。RXTRG模式则在RX FIFO中的数据达到预设水平如半满时中断允许你一次性读取多个数据效率更高。特别要注意的是在接收过程中如果Target返回NACK可能是地址错误或Target无响应SR.ERR和SR.ADRACK/DATACK位会置位并产生NACK中断。此时控制器会自动发送停止条件你必须在中端服务程序中处理这个错误例如重试或报告错误。3. 目标Target模式详解响应与数据吞吐当你的设备作为总线上的从设备时就需要配置为目标模式。目标模式的初始化步骤比控制器模式稍多因为它需要设定自身的“身份”地址和行为准则。3.1 目标模式初始化与地址配置目标模式的使能位于I2CT的CTR.ENABLE位。在使能前需要先配置几个关键参数。首先是TPR寄存器但它在这里的作用与控制器模式不同。在目标模式下TPR主要用于时钟超时检测Timeout A/B而不是产生SCL时钟时钟由Controller提供。你需要根据系统时钟和期望的超时时间来配置TCNTLA和TCNTLB。核心的配置是目标地址寄存器OAR以及高级实例中的OAR2。你的设备将响应写入这些寄存器的地址。可以配置一个或两个地址例如一个用于常规通信另一个用于广播地址或第二功能。ACKCTL寄存器用于配置应答行为是自动发送ACK还是由软件手动控制ACK覆盖。在手动控制模式下ACKCTL.ACKOEN1你可以在RXDONE中断中读取数据后再决定是回复ACK准备接收下一字节还是NACK请求停止。3.2 目标接收模式被动接收数据当Controller发送的地址与OAR中地址匹配且R/W位为0写时设备进入目标接收模式。此时数据由Controller驱动目标设备在SCL的上升沿锁存SDA上的数据。使用RXDONE中断是最基础的方式。每接收完一个字节硬件就会产生RXDONE中断。在中断服务程序中你从RXDATA寄存器读取数据然后根据ACKCTL的配置决定是否以及如何发送ACK。如果启用了ACK覆盖你需要在中断中手动设置ACKCTL.ACKOVAL0为ACK1为NACK。这种方式软件介入深能处理每一个字节但效率低因为每个字节都会产生中断且可能因为软件响应慢而导致时钟拉伸SCL被拉低等待。使用RXTRG中断是提升吞吐量的关键。你可以通过IFLS.RXIFSEL设置一个触发水平例如“FIFO中有4个数据”。当接收到的数据填满FIFO达到这个水平时才触发一次RXTRG中断。在中断服务程序中你可以连续从FIFO中读取多个数据。同时将ACKCTL配置为自动ACKACKOEN0这样硬件会在每个字节后自动回复ACK无需软件干预避免了时钟拉伸使得Controller可以全速发送数据。这对于接收大量连续数据如从传感器读取采样流的场景非常高效。流程图Figure 26-21清晰地展示了如何用rxCount变量跟踪接收到的字节数并在缓冲区满时丢弃后续数据读取到哑变量。3.3 目标发送模式响应控制器读取请求当Controller发送的地址与OAR中地址匹配且R/W位为1读时设备进入目标发送模式。此时目标设备需要将数据放到SDA线上由Controller读取。使用TXDONE中断时你需要在START中断检测到起始条件或上一次TXDONE中断中将下一个要发送的字节写入TXDATA寄存器。硬件发送完该字节后会触发TXDONE中断你再写入下一个字节如此循环。这种方式同样简单但效率不高。使用TXTRG中断则能实现高效发送。你预先在TX FIFO中填充一部分数据。当Controller发起读请求硬件开始发送FIFO中的数据。随着数据被送出FIFO空间增加当空间达到IFLS.TXIFSEL设定的阈值如“FIFO半空”时触发TXTRG中断。你在中断服务程序中继续填充FIFO直到所有数据准备完毕。同时你需要监控txCount以确保不会溢出。这种“水车”式补给能确保在Controller连续读取时数据源源不断最大限度地利用总线带宽。实操心得在目标发送模式下一个常见的坑是数据准备不及时导致NACK。例如如果你使用TXDONE模式但在中断服务程序中准备数据太慢导致SCL被拉伸过久一些不耐烦的Controller可能会超时并发送NACK。因此对于任何可能耗时的操作如从闪存读取数据务必提前将数据准备好到缓冲区中断服务程序只负责从缓冲区搬运到TXDATA做到快进快出。4. 中断与事件系统深度剖析UNICOMM-I2C模块的中断系统是其高效运作的大脑。它分为两大块CPU中断和DMA触发事件。理解每个中断源的含义和触发时机是编写稳健驱动程序的基础。4.1 CPU中断源全解与优先级管理模块通过CPU_INT寄存器组管理多达15个Controller或17个Target中断源。这些中断在IIDX寄存器中有一个固定的硬件优先级当多个中断同时发生时优先级最高的中断索引号会被反映在IIDX.STAT字段中。软件读取IIDX寄存器会自动清除对应的原始中断状态RIS和屏蔽中断状态MIS位。对于Controller关键的中断包括RXDONE/TXDONE接收/发送完成中断。当CTR.BLEN设定的字节数传输完成时触发。这是进行事务收尾处理如检查状态、准备下一事务的主要信号。RXTRG/TXTRG接收/发送触发中断。由IFLS寄存器配置的FIFO水平触发。这是实现流控和高效DMA传输的核心。NACK地址或数据未被应答中断。这是错误处理的关键一旦发生通常意味着通信链路有问题需要检查目标设备状态、地址或总线连接。START/STOP检测到总线上的起始/停止条件中断。在多主系统中非常有用可以用于监控总线活动。ARBLOST仲裁丢失中断。在多Controller竞争总线时只有一个能胜出失败的Controller会触发此中断软件应转入接收模式或等待重试。TIMEOUTA/B时钟低电平/高电平超时中断。用于检测总线挂死例如Target故障持续拉低SCL。对于Target除了上述类似中断还有几个特有的GENCALL广播地址中断。当Controller发送广播地址0x00时触发。RX_OVFL/TX_UNFLFIFO溢出/下溢中断。表明软件处理速度跟不上硬件是调试流控问题的重要标志。配置中断时你需要通过IMASK寄存器使能关心的中断源然后在中断服务程序中先读取IIDX识别中断源再执行相应操作最后通过ICLR寄存器清除中断虽然读IIDX已清除RIS/MIS但显式清除是好习惯。4.2 DMA触发与高效数据搬运实战中断虽然解放了轮询但数据搬运本身仍占用CPU。UNICOMM-I2C的DMA_TRIG_RX和DMA_TRIG_TX事件发布器可以将RXTRG/TXTRG事件直接连接到DMA控制器实现数据在内存和I2C FIFO之间的自动搬运。其配置流程如下配置DMA通道设置DMA的源地址对于接收是I2C RXDATA寄存器地址对于发送是内存缓冲区地址、目标地址对于接收是内存缓冲区地址对于发送是I2C TXDATA寄存器地址、传输数据宽度和长度。配置I2C的DMA触发在DMA_TRIG_RX/TX组的IMASK寄存器中使能RXTRG或TXTRG作为DMA触发源。配置I2C的FIFO触发水平通过IFLS寄存器设置RXIFSEL/TXIFLSEL这决定了何时产生DMA触发信号。例如设置RXIFSEL为“FIFO1/2满”那么当接收FIFO中数据达到半满时就会向DMA发送一个请求。启动传输配置并启动DMA通道然后启动I2C事务对于Controller或等待Controller访问对于Target。当DMA完成预设长度的传输后它会向I2C模块发送一个DMA_DONE信号这会触发CPU_INT中的DMA_DONE_RX或DMA_DONE_TX中断通知CPU进行后续处理如校验数据、启动下一轮传输。这种“DMA中断”的模式几乎将CPU从数据搬运中完全解放。例如在一个需要连续采集1000个字节传感器数据的场景中你可以设置DMA传输长度为1000I2C接收FIFO触发水平为半满。之后整个1000字节的接收过程无需CPU干预仅在全部完成后产生一次DMA_DONE中断。CPU利用率极低且总线带宽得到充分利用。5. 关键状态与错误处理从寄存器到代码稳定的I2C驱动离不开对状态的严密监控和对错误的妥善处理。UNICOMM-I2C的SR状态寄存器就是你的“仪表盘”。5.1 状态寄存器SR位深度解读与使用场景SR寄存器提供了模块内部状态机和总线状态的实时快照。以下几个位在调试和运行中至关重要BUSY指示I2C控制器状态机是否繁忙。在启动新事务CTR.FRM_START1前必须确保此位为0。它是判断一次传输是否完全结束的可靠标志。BUSBSY指示外部I2C总线是否繁忙。在多主系统中发起传输前必须检查此位是否为0以避免破坏他人通信。它与BUSY不同BUSY反映内部状态机BUSBSY反映物理线路。IDLE状态机空闲标志。比BUSY更“闲”表示不仅没有活跃事务连内部清理工作都已完成。在某些需要彻底复位状态的场景下有用。ERR综合错误标志。当ADRACK地址无应答或DATACK数据无应答任一发生时此位都会置1。它是快速判断事务是否出错的第一个检查点。ADRACK/DATACK更具体的NACK指示。帮助你区分是地址错误还是数据阶段出错。ARBLST仲裁丢失。仅在多Controller模式下有意义。RXFE/RXFF/TXFE/TXFFFIFO空/满状态。在查询式编程或调试FIFO触发逻辑时非常有用。一个健壮的事务启动流程应该是等待SR.BUSY 0- 如果是多主等待SR.BUSBUSY 0- 配置TA、CTR等寄存器 - 写入数据到FIFO - 置位CTR.FRM_START。在中断服务程序中处理完数据后也应检查SR.ERR等位以确保事务成功。5.2 常见错误排查与恢复策略I2C通信失败是嵌入式开发中的常客。结合SR寄存器和中断我们可以系统地定位问题。通信完全无响应NACK错误现象持续收到NACK中断SR.ERR和SR.ADRACK置位。排查硬件层面首先用示波器或逻辑分析仪检查SCL和SDA波形。确认上拉电阻是否合适通常4.7kΩ-10kΩ线路是否有短路、断路。测量目标设备供电是否正常。地址问题确认TA.ADDR配置是否正确7位地址还是10位地址地址值是否左移。许多设备有多个地址选择需查阅数据手册。目标设备忙某些设备在上电初始化、内部写周期期间会拉低SDA时钟拉伸或不应答。查看目标设备数据手册中的时序要求增加启动后的延迟。总线冲突检查是否有其他设备意外拉低了总线。可以尝试在初始化前将MCU的I2C引脚配置为高阻输入用BMON寄存器读取SCL和SDA的电平如果应为高电平却读回低电平则存在总线冲突。数据错误或丢失现象能收到ACK但数据内容不对或丢失部分数据。排查时钟速度过快检查TPR寄存器配置的SCL频率是否超过目标设备或总线布线所能支持的最高速度。尤其在长导线或有较大容性负载时应降低速率。FIFO溢出/下溢如果启用了RXTRG/TXTRG中断或DMA检查是否触发了RX_OVFL或TX_UNFL中断。这通常意味着软件处理数据的速度跟不上硬件。需要优化中断服务程序效率或调整FIFO触发水平给软件更充裕的反应时间。中断服务程序过长导致未能及时响应TXTRG中断填充FIFO或未能及时从RX FIFO取走数据。确保ISR只做最必要的操作如搬运数据指针将复杂处理放到主循环中。总线锁死SCL被拉低现象程序卡住SR.BUSBSY一直为1可能触发TIMEOUTA中断SCL低超时。排查与恢复目标设备故障最常见原因。目标设备可能因程序跑飞或硬件故障而持续拉低SCL。恢复策略UNICOMM-I2C模块的时钟超时检测TIMEOUTA/B正是为此设计。当检测到SCL被异常拉低超过设定时间会触发超时中断。在超时中断服务程序中软件可以尝试进行总线恢复操作。一种常见的“软件复位”方法是先将I2C模块的CR.ENABLE位清零以禁用模块这会使MCU引脚释放总线然后控制GPIO模拟产生几个SCL时钟脉冲同时监控SDA直到SDA被释放为高电平最后再重新使能I2C模块。这个过程可以尝试将故障设备“踢”出总线。5.3 复位与调试模式下的行为理解模块在复位和仿真调试时的行为对系统设计和调试至关重要。软件复位通过特定的复位控制寄存器RSTCTL进行。关键点必须在事务终止后才能发起复位。复位后所有寄存器恢复默认值状态位SR被清除。软件需要重新完整初始化模块。硬件复位会使IO引脚进入高阻态。由于I2C总线依赖上拉电阻复位期间和复位后瞬间SCL和SDA线会被外部上拉电阻拉高这通常是安全的空闲状态。仿真调试模式通过PDBGCTL寄存器的FREE和SOFT位控制。当芯片被调试器暂停Halt时FREE1模块继续运行不影响实时通信。这是在线调试时的首选模式。FREE0, SOFT1优雅暂停。模块会完成当前正在传输的帧直到遇到NACK或STOP条件然后才停止并将总线驱动到空闲状态。移除暂停后通信从中断处恢复。这对于调试通信过程而不破坏数据帧非常有用。FREE0, SOFT0不支持。在调试涉及I2C通信的代码时我强烈建议将调试模式设置为“优雅暂停”。这样可以避免在单步调试时因I2C模块突然停止而违反总线协议导致目标设备状态错乱或总线锁死。6. 高级配置与性能优化技巧掌握了基本操作后一些高级功能和优化技巧能让你的I2C驱动更加鲁棒和高效。6.1 时钟配置、毛刺滤波与超时保护TPR寄存器与时钟计算TPR值决定了SCL时钟频率。计算公式为SCL_Period (1 TPR) * (SCL_LP SCL_HP) * I2C_CLK_Period。其中SCL_LP和SCL_HP通常是硬件固定值如6和4。例如I2C功能时钟为20MHz周期50ns目标SCL为100kHz周期10us。计算可得10us (1TPR)*(64)*50nsTPR 19。务必根据实际时钟源准确计算。毛刺滤波GFCTL在电气环境嘈杂的场合总线上的毛刺可能被误认为是起始/停止条件或数据位。GFCTL寄存器的DGFSEL字段可以设置数字滤波器的宽度以功能时钟周期计AGFEN位可以启用模拟滤波器有效滤除短脉冲毛刺增强通信可靠性。但要注意滤波会增加信号延迟在高速模式下需权衡。超时控制TIMEOUT_CTL这是防止总线挂死的安全网。TIMEOUTA监控SCL低电平时间TIMEOUTB监控SCL高电平时间。一旦超过设定值就会触发超时中断。在中断中你可以尝试恢复总线。计算超时值需要结合最大允许的时钟拉伸时间。例如如果某个目标设备最多可拉伸时钟300us那么TIMEOUTA应设置得比300us稍大一些以避免误报。6.2 多控制器仲裁与时钟拉伸多控制器模式CR.MCTL当总线上有多个MCU都可能作为Controller时需要将此位置1。在此模式下SCL高电平时间从检测到SCL线实际变高开始计时而不是从本机驱动变高开始计时这确保了多个Controller之间的时钟同步和公平仲裁。时钟拉伸CR.CLKSTRETCHI2C协议允许Target在需要更多时间处理数据时拉低SCL时钟拉伸。Controller必须等待SCL被释放。使能CLKSTRETCH位会让Controller检测并等待这一过程这是符合协议的标准行为。但是如果你确信总线上的所有Target都不使用时钟拉伸可以禁用此功能CLKSTRETCH0这样Controller在驱动SCL变高后会立即开始下一个位周期可以略微提高总线速度。6.3 PEC包错误校验与快速命令PEC包错误校验通过PECCTL寄存器使能。它使用CRC-8算法多项式x^8 x^2 x^1 1对除起止位和应答位外的所有位进行计算并在数据包末尾附加一个PEC字节。在接收端模块会自动计算并比对PEC如果错误则产生PEC_RX_ERR中断并发送NACK。这对于要求高可靠性的通信非常有用但会增加一个字节的开销。快速命令Quick Command这是一种特殊的控制器事务长度CTR.BLEN设为0只发送地址和读写位紧接着就是停止条件。它用于向目标设备发送一个简单的“唤醒”、“复位”等命令而无需传输数据字节。配置时设置TA.DIR决定读写位CTR.START1CTR.STOP1CTR.FRM_START1即可。7. 实战代码框架与避坑指南理论最终要落到代码上。这里给出一个基于UNICOMM-I2C模块的控制器发送使用TXTRGDMA和目标接收使用RXTRG中断的简化代码框架和关键注意事项。7.1 控制器发送示例使用DMA// 假设I2C0 为 Controller DMA 通道0 用于 TX // 目标发送 buffer 中的 128 字节数据到地址 0x50 的设备 // 1. 初始化 I2C Controller I2C0-CR ~CR_ENABLE_MASK; // 先禁用模块 // 配置时钟分频、TPR等... I2C0-CR | CR_MCTL_MASK; // 使能多控制器模式如果需要 I2C0-CR | CR_ENABLE_MASK; // 使能模块 // 2. 配置 DMA // 设置 DMA 通道0源地址 发送buffer地址目标地址 (I2C0-TXDATA) // 传输宽度字节传输长度128使能自动递增源地址 // 触发源选择为 I2C0 的 TXTRG 事件 DMA_ConfigChannel(0, SRC_ADDR, (uint32_t)(I2C0-TXDATA), ...); DMA_SetTriggerSource(0, DMA_TRIG_SRC_I2C0_TXTRG); // 3. 配置 I2C 发送参数 I2C0-TA (0x50 1) | (0 0); // 地址0x50 DIR0 (写) I2C0-CTR (127 CTR_BLEN_SHIFT) | CTR_STOP_MASK | CTR_START_MASK; // BLEN128-1, 产生STOP和START // 4. 配置 FIFO 触发水平和中断 I2C0-IFLS (2 IFLS_TXIFLSEL_SHIFT); // TX FIFO 1/2 空时触发 (可根据FIFO深度调整) I2C0-CPU_INT.IMASK | IMASK_TXTRG_MASK | IMASK_TXDONE_MASK; // 使能 TXTRG 和 TXDONE 中断 I2C0-DMA_TRIG_TX.IMASK | IMASK_TXTRG_MASK; // 使能 DMA 触发 // 5. 启动 DMA 和 I2C 事务 DMA_EnableChannel(0); // 注意需要先确保总线空闲 (SR.BUSY0 SR.BUSBUSY0) I2C0-CTR | CTR_FRM_START_MASK; // 启动事务 // 6. 中断服务程序 (ISR) 处理 void I2C0_IRQHandler(void) { uint32_t iidx I2C0-CPU_INT.IIDX; switch(iidx) { case IIDX_TXDONE: // 128字节发送完成 DMA_DisableChannel(0); I2C0-CPU_INT.ICLR | ICLR_TXDONE_MASK; // 清除中断 // ... 后续处理如发送完成标志 break; case IIDX_NACK: // 处理错误 I2C0-CPU_INT.ICLR | ICLR_NACK_MASK; // ... 错误恢复逻辑 break; // ... 其他中断处理 } }7.2 目标接收示例使用RXTRG中断// 假设I2C1 为 Target 地址为 0x68 // 目标接收数据到 rx_buffer[256] volatile uint32_t rx_count 0; uint8_t rx_buffer[256]; // 1. 初始化 I2C Target I2C1-CR ~CR_ENABLE_MASK; // 配置TPR用于超时、毛刺滤波等... I2C1-OAR 0x68 1; // 7位地址 I2C1-ACKCTL 0; // 自动ACK I2C1-IFLS (2 IFLS_RXIFLSEL_SHIFT); // RX FIFO 1/2 满时触发 I2C1-CPU_INT.IMASK | IMASK_RXTRG_MASK | IMASK_STOP_MASK; I2C1-CR | CR_ENABLE_MASK; // 2. 中断服务程序 void I2C1_IRQHandler(void) { uint32_t iidx I2C1-CPU_INT.IIDX; if(iidx IIDX_RXTRG) { // FIFO达到触发水平一次性读取多个数据 while(!(I2C1-SR SR_RXFE_MASK)) { // 当RX FIFO非空时 if(rx_count 256) { rx_buffer[rx_count] I2C1-RXDATA; } else { // 缓冲区已满丢弃数据但必须读出来以清空FIFO volatile uint8_t dummy I2C1-RXDATA; } } I2C1-CPU_INT.ICLR | ICLR_RXTRG_MASK; } else if(iidx IIDX_STOP) { // 检测到停止条件一帧数据接收完毕 // 可以处理 rx_buffer 中的数据 rx_count 0; // 重置计数器准备接收下一帧 I2C1-CPU_INT.ICLR | ICLR_STOP_MASK; } }7.3 避坑指南与最佳实践初始化顺序务必遵循“先配置后使能”的原则。在修改关键参数如地址、时钟前先清除CR.ENABLE位配置完成后再置位ENABLE。对于TPR等寄存器可能在ENABLE0时也无法写入需要检查参考手册。FIFO清除在重新开始一次通信或切换模式前如果FIFO中可能有残留数据应使用IFLS.RXCLR和IFLS.TXCLR位进行清除避免旧数据干扰新通信。中断清除虽然读取IIDX会清除RIS/MIS但良好的习惯是在中断服务程序末尾显式地写ICLR寄存器来清除已处理的中断。这可以避免某些极端情况下的中断残留。状态检查在启动任何事务写FRM_START前双重检查SR.BUSY和SR.BUSBUSY。在多任务或中断环境中前一个事务可能刚刚结束状态位还没来得及更新。超时处理务必使能并合理配置TIMEOUTA/B。在中断服务程序中处理超时执行总线恢复程序。一个没有超时保护的I2C驱动在目标设备故障时可能导致整个系统挂起。电源与复位管理当目标设备可能被断电或复位时Controller的通信尝试会持续收到NACK。驱动中应加入重试计数和延迟机制避免陷入死循环。在多次失败后可以尝试发送一个STOP条件并重新初始化I2C模块有时能帮助总线恢复。调试工具投资一个逻辑分析仪如Saleae或带有I2C解码功能的示波器。它们能直观地展示总线上的起始、停止、地址、数据、ACK/NACK位是调试I2C问题无可替代的工具。结合MCU的寄存器状态可以快速定位问题是出在软件配置、硬件连接还是目标设备本身。