TI I2C模块寄存器深度解析:从时钟配置到数据流控制
1. I2C模块寄存器全景解析:从总线协议到硬件实现
搞嵌入式开发,尤其是和传感器、EEPROM、RTC这些外设打交道,I2C总线绝对是绕不开的。两根线(SCL时钟线、SDA数据线)搞定通信,听起来简单,但真要把底层驱动调通、调稳,尤其是面对不同厂商、不同速度等级的从设备时,对I2C控制器内部寄存器的理解深度,直接决定了你是能快速定位问题,还是只能对着示波器抓耳挠腮。很多人觉得会用现成的库函数就行,但一旦遇到时序不匹配、通信断续、从机无应答这些“玄学”问题,如果不清楚ICCLKH、ICMDR这些寄存器位到底在控制什么,排查起来就像盲人摸象。
我这些年调试过不少TI的DSP和微控制器,其I2C模块功能强大但寄存器也相对复杂。这份TI官方文档的寄存器描述,可以说是把I2C模块的“五脏六腑”都剖开给你看了。它不仅仅是一份寄存器位域说明,更是一份理解I2C硬件控制器如何将协议标准转化为具体电平和时序的操作手册。从最基础的时钟生成(ICCLKL/H),到数据传输的缓冲(ICDRR/ICDXR),再到决定每一次通信行为模式的“大脑”(ICMDR),每一个寄存器都扮演着关键角色。理解它们,你就能从“配置参数”的层面,深入到“控制硬件状态机”的层面,这才是写出稳定、高效、可移植驱动代码的底气。
2. 时钟与速率控制:ICCLKL/H与ICPSC寄存器深度剖析
I2C通信的稳定性,一半以上取决于时钟(SCL)信号的质量。这个时钟并非直接来自系统主频,而是经过多级分频得到的。TI的I2C模块通过三个寄存器协同工作来精确塑造SCL波形:预分频寄存器(ICPSC)负责第一级粗调,而高/低电平时间分频寄存器(ICCLKH和ICCLKL)则负责第二级精调,共同决定了最终的通信速率和信号占空比。
2.1 时钟生成链路与预分频器(ICPSC)
I2C模块的时钟源通常是芯片的系统时钟或外设总线时钟,我们称之为输入时钟(I2C input clock)。这个频率可能很高(比如几十甚至上百MHz),而I2C标准模式(100kbps)、快速模式(400kbps)乃至快速模式+(1Mbps)所需的SCL频率要低得多。因此,第一道工序就是通过ICPSC寄存器进行预分频。
ICPSC寄存器只有低8位(IPSC字段)有效,它是一个8位的分频系数。其计算公式非常直接:模块时钟频率 = 输入时钟频率 / (IPSC + 1)。这里得到的“模块时钟”(prescaled module clock)是后续进行高/低电平时间细分的基准时钟。
注意:ICPSC的配置有一个关键限制——必须在I2C模块处于复位状态(即ICMDR寄存器中的IRS位为0)时进行。一旦你通过置位IRS=1启动了模块,再修改IPSC值是无效的。这个设计是为了避免在通信过程中突然改变基准时钟,导致时序彻底混乱。所以,标准的初始化流程一定是:先保持IRS=0,配置好ICPSC、ICCLKL、ICCLKH等所有时序相关寄存器,最后再置位IRS=1使能模块。
举个例子,假设你的DSP输入时钟是100MHz,目标I2C模块时钟希望是10MHz,以便后续产生400kbps的SCL。那么计算IPSC值:IPSC = (100MHz / 10MHz) - 1 = 9。你需要向ICPSC寄存器写入0x09。这个10MHz的模块时钟,就是接下来ICCLKH和ICCLKL的“尺子”。
2.2 SCL波形精细雕刻:ICCLKH与ICCLKL
得到了模块时钟,下一步就是用这把“尺子”去度量SCL信号的高电平和低电平时间。这就是ICCLKH(高电平时间分频寄存器)和ICCLKL(低电平时间分频寄存器)的工作。它们分别有一个16位的字段:ICCH和ICCL。
SCL周期计算公式:
- SCL高电平时间
T_high = (ICCH + d) * T_mod - SCL低电平时间
T_low = (ICCL + d) * T_mod - SCL总周期
T_scl = T_high + T_low = (ICCH + ICCL + 2d) * T_mod - SCL频率
F_scl = 1 / T_scl = F_mod / (ICCH + ICCL + 2d)
其中,T_mod是模块时钟周期,F_mod是模块时钟频率,d是一个固定延迟参数,在TI的不同系列芯片中通常为固定值(例如,在一些C2000系列中d=7)。这个d代表了模块内部路径的固定延迟,必须计入,否则计算出的实际频率会偏快。
占空比控制:标准的I2C协议要求SCL信号的占空比(高电平时间占周期的比例)大致为50%。但有些从设备对占空比有特殊要求。通过独立设置ICCH和ICCL,你可以灵活调整占空比。例如,如果你发现某个传感器在SCL高电平较窄时读取更稳定,就可以适当减小ICCH的值(但需满足I2C协议规定的最小高电平时间)。
实操配置步骤:
- 确定目标SCL频率:根据从设备支持的最高速率选择,如100kbps, 400kbps。
- 计算模块时钟频率:通常,模块时钟频率至少是目标SCL频率的8-10倍,以保证细分精度。例如,对于400kbps,选择8MHz以上的模块时钟比较合适。
- 配置ICPSC:根据输入时钟和步骤2的模块时钟,计算并写入IPSC值。切记在IRS=0时操作。
- 计算ICCH和ICCL:这是关键。假设
F_mod=10MHz(T_mod=0.1us),d=7,目标F_scl=400kHz(T_scl=2.5us)。- 总的分频系数
N_total = T_scl / T_mod = 2.5us / 0.1us = 25。 - 根据公式
N_total = ICCH + ICCL + 2d,得出ICCH + ICCL = N_total - 2d = 25 - 14 = 11。 - 为了接近50%占空比,可以设置
ICCH = 5,ICCL = 6。代入验证:T_high = (5+7)*0.1us=1.2us,T_low=(6+7)*0.1us=1.3us,T_scl=2.5us,频率400kHz,占空比48%。
- 总的分频系数
- 写入ICCLKH和ICCLKL:同样,在IRS=0时,将计算好的ICCH和ICCL值分别写入这两个寄存器。
避坑指南:ICCH和ICCL的有效范围是1-65536(对应寄存器值0-0xFFFF)。但绝对不能设置为0。如果设置为0,意味着高或低电平时间仅为d个模块时钟,通常无法满足I2C协议规定的最小脉冲宽度,必然导致通信失败。我习惯上设置的最小值不会低于5。
2.3 时钟配置的常见陷阱与排查
问题一:通信速率远低于预期。
- 现象:用示波器测量SCL频率,发现只有计算值的1/2甚至1/4。
- 排查:首先检查
d值。你使用的芯片数据手册或技术参考手册的I2C章节一定会明确给出这个值。如果忽略了d,你的计算就会出错。其次,确认你写入寄存器的值是否正确。ICCLKL和ICCLKH是16位寄存器,如果你用8位赋值函数,可能会只写了低8位,高8位是随机值,导致分频系数巨大。 - 解决:重新核对计算公式,特别是
d的取值。使用内存查看工具,确认写入寄存器的值与你计算的值一致。
问题二:通信不稳定,偶尔丢数据。
- 现象:大部分通信正常,但在长时间或大数据量传输时出现偶发性错误。
- 排查:重点检查SCL信号的上升/下降时间和过冲。ICCH/ICCL设置得过小,会导致高/低电平时间太短,如果PCB走线较长或负载电容较大,信号边沿来不及达到稳定的逻辑电平,就会造成采样错误。用示波器放大SCL信号的边沿观察。
- 解决:适当增大ICCH和ICCL的值,降低SCL频率。或者,在硬件上,确保SCL和SDA线路上有适当的上拉电阻(通常4.7kΩ-10kΩ),并尽量缩短走线。
问题三:从设备无应答(NACK)。
- 现象:主机发送地址后,从机不回ACK。
- 排查:除了地址错误,时钟问题也是主因。如果SCL频率超过了从设备支持的最高频率,从设备可能无法跟上节奏。用示波器测量SCL频率是否超标。
- 解决:降低ICPSC的分频系数,提高模块时钟频率��然后同比增ICCH和ICCL,以降低最终的SCL频率。确保频率在从设备规格书范围内。
3. 核心控制中枢:ICMDR模式寄存器逐位解读
如果说时钟寄存器是I2C模块的“心脏”,那么ICMDR(I2C Mode Register)就是其“大脑”。它集成了决定I2C每一次操作行为的几乎所有关键控制位。配置不当,轻则通信失败,重则导致总线锁死。我们把它拆开揉碎了看。
3.1 模式与状态控制位组
这一组位决定了I2C模块的基本身份和状态。
- IRS (I2C Reset):这是总开关。0-模块复位/禁用;1-模块使能。黄金法则:在配置任何其他寄存器(特别是ICPSC, ICCLKL/H)之前,必须确保IRS=0。在通信间歇,如果你想彻底重置I2C状态机(比如从错误中恢复),也需要先清零IRS,再重新初始化,最后置位IRS。警告:在传输过程中突然清零IRS可能导致总线挂死(SDA/SCL呈高阻态),因为模块会突然释放对引脚的控制。
- MST (Master Mode):主从模式选择。0-从模式;1-主模式。模块上电后默认为从模式。当你作为主机发起传输时,需要先置位MST。一个有用的特性是:当主机产生STOP条件后,硬件会自动将MST清零,模块恢复为从模式,可以监听总线。这符合I2C总线多主机的特性。
- TRX (Transmitter Mode):传输方向。0-接收器;1-发送器。注意:TRX的作用依赖于MST和FDF位,文档中的表21-16总结得非常清楚:
MST=0, FDF=0(从模式,非自由数据格式):TRX是“无关位”。从机的收发角色完全由主机发送的地址字节后的R/W位决定。MST=0, FDF=1(从模式,自由数据格式):TRX固定角色,0为接收器,1为发送器。MST=1(主模式):无论FDF为何值,TRX明确指示主机角色,0为接收器,1为发送器。
- XA (Expanded Address):地址模式。0-7位地址;1-10位地址。这个位需要与ICSAR(从机地址寄存器)的配置匹配。如果选择7位模式,ICSAR的高位被忽略。
3.2 传输流程控制位组
这组位直接指挥着START和STOP条件的产生,是控制传输流程的手柄。
- STT (START Condition):起始条件位。仅在主机模式(MST=1)下有效。软件置位STT=1,硬件会在总线上产生一个START(或Repeated START)条件,然后自动清零该位。关键点:在从机模式下,STT位必须为1,模块才会监听总线并响应主机的命令。如果你只想让模块作为主机,不作为从机,可以在主机操作完成后将STT清零。
- STP (STOP Condition):停止条件位。仅在主机模式下有效。软件置位STP=1,硬件会在当前传输序列的最后一个数据字节后的ACK周期后,产生一个STOP条件,然后自动清零该位。常见误区:STP不是立即产生STOP,而是“预约”一个STOP。它通常与数据计数器(ICCNT)配合,在计数器归零时自动产生。
- RM (Repeat Mode):重复模式。0-非重复模式,传输数据量由ICCNT寄存器指定;1-重复模式,持续传输数据直到收到STOP条件或软件强制产生STOP。这个模式对于流式数据传输(如连续读取传感器数据)非常有用,可以避免频繁操作ICCNT和STT/STP。
RM, STT, STP的组合逻辑(表21-15)是理解主机行为的关键:
RM=0, STT=1, STP=0: 启动一次传输,发送START、从机地址、传输ICCNT指定的N个数据字,然后等待。主机保持对总线的控制,SCL被拉低(时钟延展),直到你设置STP=1或发起新的START(通过再次置位STT)。RM=0, STT=1, STP=1: 启动一次传输,发送START、从机地址、传输N个数据字,然后在最后一个数据字后自动产生STOP。这是最常用的单次读写模式。RM=1, STT=1, STP=0: 启动重复模式传输,发送START、从机地址,然后持续发送/接收数据,无视ICCNT,直到你设置STP=1来终止。RM=1, STT=1, STP=1: 保留组合,无活动。
3.3 数据格式与特殊功能位
- FDF (Free Data Format):自由数据格式模式。置1使能后,I2C模块将忽略地址匹配过程,直接进行纯数据传输。在这种模式下,总线上没有地址字节和ACK/NACK位,数据帧之间仅由START和STOP条件分隔。这用于与不支持标准I2C协议但兼容其电气特性的设备通信。注意:此时主从角色必须通过TRX固定。
- BC (Bit Count):位计数。这个3位字段定义了每个数据字包含的位数(1-8位)。重要:必须与通信对端设备的数据宽度匹配。例如,与一个12位ADC通信,如果ADC数据分两次、每次8位发送,则BC应设为8。如果它一次发送12位(非标准),你可能需要设置BC=8并处理两次传输,或者使用自由数据格式。当BC=0时,代表8位数据。
- NACKMOD (NACK Mode):NACK模式。此位控制接收器(无论是主机还是从机)何时发送NACK信号。通常,接收器在每个字节后发送ACK。在主机接收器模式下,当内部数据计数器(基于ICCNT)减到0时,硬件会自动发送NACK通知从机停止发送。如果你需要提前终止传输(比如接收缓冲区满),可以手动置位NACKMOD,硬件会在下一个ACK周期发送NACK后自动清除此位。
- DLB (Digital Loopback):数字回环模式。置1后,模块内部将发送数据路径直接短接到接收路径,用于软件自测试,无需外部连接。注意:文档明确指出,DLB模式与自由数据格式模式(FDF=1)同时使能是不支持的。
- FREE:仿真模式位。当你在调试器(如CCS)中遇到断点时,此位决定I2C模块的行为。0-遇到断点时,如果SCL为低则立即停止并保持拉低;如果SCL为高则等待其变低后停止。这可以防止总线在调试期间因主机“发呆”而超时。1-运行自由,即使遇到断点也继续运行。在调试与实时性要求高的从设备通信时,建议设置为1,避免因调试暂停导致从设备超时。
4. 数据流与地址管理:从缓冲到传输
配置好了时钟和模式,数据是如何流动的呢?这涉及到数据寄存器、地址寄存器和数据计数器。
4.1 数据收发缓冲区:ICDRR与ICDXR
数据收发通过一对寄存器完成:ICDRR(数据接收寄存器)和ICDXR(数据发送寄存器)。但要注意,它们并不是直接与引脚通信的移位寄存器。
- 数据流路径:
- 发送:CPU或DMA将数据写入ICDXR。硬件在适当的时候(如前一个字节发送完成)自动将ICDXR中的数据加载到内部的**发送移位寄存器(ICXSR)**中。ICXSR负责将数据逐位移出到SDA引脚。CPU无法直接访问ICXSR。
- 接收:SDA引脚上的数据被内部的接收移位寄存器(ICRSR)逐位移入。当一个字节接收完成后,硬件自动将ICRSR中的数据拷贝到ICDRR中,供CPU或DMA读取。CPU同样无法直接访问ICRSR。
这种设计实现了双缓冲,提高了效率。CPU可以在当前字节正在发送/接收时(ICXSR/ICRSR忙),准备下一个要发送的数据到ICDXR,或读取上一个已接收的数据从ICDRR。
- 数据对齐:当BC位设置的数据位宽小于8位时(例如BC=4,表示4位数据),数据在ICDRR和ICDXR中都是右对齐的。也就是说,对于4位数据
0b1011,它存放在寄存器的低4位(D[3:0]),高4位(D[7:4])在接收时是未定义的,在发送时应写入0。这是一个常见的坑:如果你发送的数据位宽不是8位,务必确保写入ICDXR的数据是右对齐的,否则发送出去的数据会是错的。
4.2 从机地址设定:ICSAR
当I2C模块作为主机时,ICSAR寄存器存放了你想要通信的从机地址。根据ICMDR中XA位的设置,使用不同的位域:
- XA=0 (7-bit模式):仅使用ICSAR的位[6:0]。
- XA=1 (10-bit模式):使用ICSAR的位[9:0]。
关键点:在10位地址模式下,I2C协议规定地址传输分两个字节:第一个字节是11110 A9 A8 R/W,第二个字节是A7-A0。TI的硬件会自动处理这个拆分和发送过程,你只需要把完整的10位地址写入ICSAR即可。
广播地址:如果你向ICSAR写入0x00,并在7位地址模式下发起传输,主机将发送“广播呼叫”地址(0x00)。所有支持广播呼叫的从机都会应答。这在需要同时向多个设备发送相同命令时有用。
4.3 传输长度控制:ICCNT
ICCNT寄存器用于在非重复模式(RM=0)下指定要传输的数据字数量。它是一个16位寄存器,可设置1到65536个数据字(写入0代表65536)。
- 工作机制:你写入ICCNT的值会被拷贝到一个内部的递减计数器。每成功传输一个数据字(包括主机发送或接收),这个内部计数器就减1。ICCNT寄存器本身的值保持不变,方便你查询。当内部计数器减到0时,如果此时STP位被预先设置为1,硬件就会自动产生一个STOP条件。
- 与STP的配合:这是实现自动结束传输的关键。典型的主机发送流程是:1) 设置ICSAR(地址)、ICMDR(模式,如MST=1, TRX=1)、ICCNT(数据个数)。2) 置位STT=1启动传输,同时置位STP=1。3) 硬件自动发送START、地址、N个数据,然后在最后一个数据后产生STOP,整个过程无需软件干预计数器状态。
- 重复模式下的忽略:当RM=1时,ICCNT的值被忽略,传输会持续进行,直到软件设置STP=1。
5. 中断、DMA与GPIO复用:高级功能与系统集成
为了解放CPU,提高效率,TI的I2C模块提供了完善的中断和DMA支持,并且考虑了引脚复用的灵活性。
5.1 中断向量与事件管理:ICIVR
I2C模块有多个事件可以触发中断,ICIVR(中断向量寄存器)用于快速识别是哪个事件触发了当前中断。它是一个只读寄存器,读取它的操作会自动清除当前最高优先级的中断标志。如果还有其它挂起的中断,会立即产生新的中断。
INTCODE字段与中断事件:
- 0: 无中断(通常不会在中断服务程序中读到)。
- 1 (AL): 仲裁丢失。这是最高优先级中断,发生在多主机系统中,当本机试图输出高电平但检测到SDA为低时,表示有其他主机赢得了总线仲裁。硬件会自动切换到从机接收模式。
- 2 (NACK): 无应答。主机发送地址或数据后,未收到从机的ACK。
- 3 (ARDY): 寄存器访问就绪。当模块准备好接收新的控制命令(如修改ICMDR的STT/STP)时触发。
- 4 (ICRRDY): 接收数据就绪。当一个新的数据字节从ICRSR转移到ICDRR后触发,表示数据可以读取了。
- 5 (ICXRDY): 发送数据就绪。当ICDXR中的数据被转移到ICXSR后触发,表示可以写入下一个要发送的数据了。
- 6 (SCD): 检测到STOP条件。在从机模式下,检测到总线上的STOP条件时触发。
- 7 (AAS): 地址匹配(作为从机)。当模块作为从机,且接收到的地址与自身地址寄存器(ICOAR,文档中未详细列出但通常存在)匹配时触发。这是最低优先级中断。
中断服务程序(ISR)最佳实践:
// 伪代码示例 void I2C_ISR(void) { uint16_t intCode = I2C_REG->ICIVR; // 读取ICIVR,自动清除标志 switch(intCode) { case 1: // AL - 仲裁丢失 // 通常进行错误处理,可能需要重新尝试发送 I2C_REG->ICSTR |= CLEAR_AL_FLAG; // 清除状态寄存器中的AL标志 break; case 2: // NACK - 无应答 // 处理无应答错误,检查从机地址、电源、连接 I2C_REG->ICSTR |= CLEAR_NACK_FLAG; break; case 4: // ICRRDY - 收到数据 rxBuffer[rxIndex++] = I2C_REG->ICDRR; // 读取数据 if(rxIndex >= expectedLength) { // 接收完成,进行后续处理 } break; case 5: // ICXRDY - 发送就绪 if(txIndex < txLength) { I2C_REG->ICDXR = txBuffer[txIndex++]; // 写入下一个数据 } else { // 所有数据已发送完毕 } break; case 7: // AAS - 地址匹配(从机) // 根据接收到的R/W位,准备发送或接收数据 if(addressMatchedAsSlave) { // 准备响应 } break; // ... 处理其他中断 } }重要提示:一定要先读取ICIVR来确定中断源并清除标志,再进行其他操作。在发起新的传输(如置位STT)前,也必须确保已通过读取ICIVR清除了旧的中断标志,否则残留的标志可能导致误判。
5.2 DMA集成:ICDMAC
对于大批量数据传输,使用DMA可以极大减轻CPU负担。ICDMAC寄存器只有两个有效位:
- RXDMAEN: 接收DMA使能。置1后,当接收数据就绪(ICRRDY事件)时,模块会向系统DMA控制器发出接收事件信号。
- TXDMAEN: 发送DMA使能。置1后,当发送数据就绪(ICXRDY事件)时,模块会向系统DMA控制器发出发送事件信号。
文档建议这两个位始终设置为1。即使你不打算用DMA,使能这些事件也无妨。当你需要配置DMA时,只需在DMA控制器端将I2C的接收/发送事件作为触发源,并设置好源地址(ICDRR或ICDXR的地址)、目的地址和传输数量即可。ICCNT寄存器可以与DMA传输计数配合使用,实现精确的传输长度控制。
5.3 引脚复用与调试:ICPFUNC, ICPDIR, ICPDIN/OUT
I2C引脚(SDA和SCL)通常与通用GPIO复用。这组寄存器提供了在I2C功能和GPIO功能之间切换的能力,这在调试和硬件故障排查时极其有用。
- ICPFUNC (Pin Function):PFUNC0位控制两个引脚的整体功能。0-作为I2C引脚;1-作为GPIO引脚。关键警告:文档明确指出,当你想切换功能时,没有硬件保护机制。安全的操作顺序是:1) 置IRS=0,复位I2C模块,释放总线。2) 设置PFUNC0=1,切换到GPIO模式。3) 进行GPIO操作。4) 切换回I2C前,再次确保IRS=0,然后设置PFUNC0=0,最后再置IRS=1。
- ICPDIR (Pin Direction):当引脚配置为GPIO时,此寄存器控制方向。PDIR0对应SCL,PDIR1对应SDA。0-输入,1-输出。
- ICPDIN (Pin Data In):这是一个只读寄存器,反映两个引脚上的实时逻辑电平,无论当前是I2C模式还是GPIO模式。这是硬件调试的神器。当你怀疑总线锁死时,可以读取ICPDIN,查看SCL和SDA的实际电平,判断是主机卡住拉低,还是从设备拉低,或者是上拉电阻失效导致的高阻态。
- ICPDOUT (Pin Data Out):当引脚配置为GPIO输出时,向此寄存器写入数据可以控制引脚输出高或低电平。注意:由于I2C总线是开漏输出,即使配置为GPIO输出模式,芯片可能也无法通过此寄存器将引脚驱动为高电平(取决于芯片内部结构),只能拉低或释放(高阻)。读取此寄存器返回的是你写入的值,而非引脚实际电平。
利用GPIO模式进行总线修复:如果总线因某个设备故障被持续拉低(即“死锁”),你可以利用GPIO模式进行“软复位”:
- 将I2C模块置位复位(IRS=0)。
- 切换引脚到GPIO输出模式(PFUNC0=1,PDIR0/1=1)。
- 通过ICPDOUT控制SCL引脚,手动产生9个或更多时钟脉冲(先拉低再拉高),同时保持SDA为高(或释放)。
- 这个操作可以“冲刷”可能卡在数据传输中的从设备,使其超时复位。
- 操作完成后,切换回I2C模式(PFUNC0=0,IRS=0),然后重新初始化I2C模块。
6. 实战配置流程与典型问题排查
理解了所有寄存器,我们来看一个完整的I2C主机发送初始化配置流程,并总结一个常见问题排查表。
6.1 主机发送初始化配置示例
假设目标:以100kHz速率,向地址为0x50的EEPROM发送3个字节数据。
- 关闭模块:
ICMDR.IRS = 0。 - 配置时钟:
- 系统输入时钟
F_in = 50MHz。 - 目标SCL频率
F_scl = 100kHz。 - 选择模块时钟
F_mod = 10MHz(足够细分)。 - 计算
IPSC = (50MHz / 10MHz) - 1 = 4。写入ICPSC = 0x0004。 - 假设
d = 7。 - 计算总系数
N_total = F_mod / F_scl = 10MHz / 100kHz = 100。 ICCH + ICCL = N_total - 2d = 100 - 14 = 86。- 为接近50%占空比,设
ICCH = 43, ICCL = 43。 - 写入
ICCLKH = 43,ICCLKL = 43。
- 系统输入时钟
- 配置从机地址:7位模式,地址0x50。写入
ICSAR = 0x0050。 - 配置数据计数:发送3个字节。写入
ICCNT = 0x0003。 - 配置模式寄存器:
BC = 0(8位数据)。FDF = 0(标准地址模式)。STB = 0(非START字节模式)。IRS = 0(保持复位,稍后启动)。DLB = 0(禁用回环)。RM = 0(非重复模式)。XA = 0(7位地址)。TRX = 1(发送器模式)。MST = 1(主机模式)。STP = 1(传输完成后自动产生STOP)。STT = 0(暂不启动)。FREE = 1(调试时自由运行)。NACKMOD = 0(正常ACK模式)。- 组合值假设为
ICMDR = 0x4A20(具体值取决于位域位置,需核对)。
- 使能中断/DMA:写入
ICDMAC = 0x0003(使能TX和RX DMA事件,即使不用DMA也建议开启)。配置中断控制器,使能所需中断(如ARDY, ICXRDY, NACK)。 - 启动模块:
ICMDR.IRS = 1。 - 启动传输:
ICMDR.STT = 1。 - 写入数据:在ICXRDY中断服务程序中,或查询状态位后,依次将3个字节数据写入
ICDXR。
6.2 I2C通信故障排查速查表
| 现象 | 可能原因 | 排查步骤与解决方法 |
|---|---|---|
| 从机无应答 (NACK) | 1. 从机地址错误。 2. 从机未上电或硬件连接问题。 3. SCL频率超出从机范围。 4. 总线被锁死(SDA被拉低)。 | 1. 核对从机数据手册地址(注意7/10位,R/W位)。 2. 检查电源、地线、上拉电阻(通常4.7kΩ-10kΩ)。用万用表测电压。 3. 用示波器测量SCL实际频率,调整ICCLKL/H。 4. 读取ICPDIN,看SDA是否恒低。尝试GPIO时钟冲刷法。 |
| 仲裁丢失 (AL) | 多主机系统中,另一主机同时发起传输并赢得仲裁。 | 1. 检查多主机协议设计,增加随机延迟重试。 2. 在AL中断服务程序中,重置状态,等待随机时间后重发。 |
| 数据错误 | 1. 数据位宽(BC)设置不匹配。 2. 数据在ICDXR中没有右对齐(当BC<8时)。 3. 时序裕量不足,建立/保持时间违规。 | 1. 确认对端设备数据格式,调整BC位。 2. 确保写入ICDXR的数据位于低BC位。 3. 用示波器检查SDA相对SCL的时序。适当降低SCL频率(增大ICCH/ICCL)。 |
| 通信断续/不稳定 | 1. 电源噪声或地线干扰。 2. 上拉电阻过大,导致上升沿过慢。 3. 总线电容过大,导致边沿变缓。 4. 中断服务程序执行时间过长,错过响应。 | 1. 加强电源滤波,优化PCB布局,缩短走线。 2. 减小上拉电阻(如从10kΩ换为4.7kΩ),但需注意驱动能力。 3. 减少总线上的设备数量,或使用更快的模式(需设备支持)。 4. 优化ISR,或使用DMA传输。检查中断优先级。 |
| 只能发送/接收一次 | 1. 重复模式(RM)使用不当。 2. 数据计数器(ICCNT)与STP位配合错误。 3. 中断标志未及时清除。 | 1. 单次传输用RM=0, STT=1, STP=1。流传输用RM=1, STT=1, STP=0,结束时手动STP=1。 2. 确认ICCNT设置正确,且STP在启动前已置位(对于自动停止)。 3. 在ISR中务必先读取ICIVR清除标志。 |
| 调试时总线挂死 | 1. 在传输中复位模块(IRS=0)。 2. 程序跑飞,SCL被意外拉低且不释放。 3. 从设备故障。 | 1. 确保只在空闲时操作IRS位。 2. 增加看门狗。使用ICPDIN检查引脚状态。 3. 隔离从设备,用GPIO模式手动产生时钟修复。 |
调试I2C,示波器或逻辑分析仪几乎是必需品。重点观察几个关键点:START条件(SDA在SCL高时变低)、地址字节(尤其是第一个字节)、ACK位(第9个时钟周期SDA是否为低)、STOP条件(SDA在SCL高时变高)。任何一个环节的波形异常,都能对应到具体的配置或硬件问题。把寄存器理解透彻,再结合工具观察波形,大部分I2C问题都能迎刃而解。