深入解析TI C672x DSP I2C模块:从寄存器配置到实战调试
1. 项目概述与I2C总线核心价值
在嵌入式系统开发,尤其是涉及复杂信号处理的应用中,如何高效、可靠地连接主处理器与周边的传感器、编解码器、存储器等外设,始终是架构设计的关键一环。从业十多年,我见过太多项目因为通信接口选择不当或配置不深,导致后期调试困难、系统不稳定。今天,我们就来深入聊聊在TI的TMS320C672x系列高性能浮点DSP上,那个看似简单却至关重要的内置外设——I2C模块。
I2C,全称Inter-Integrated Circuit,是一种由Philips(现NXP)推出的两线制串行通信总线。它的魅力在于极简的物理连接(仅需SDA数据线和SCL时钟线)和强大的寻址能力,特别适合板级芯片间的短距离、中低速通信。在C672x这类专注于音频处理、电机控制或精密测量的DSP系统中,I2C常被用来配置音频编解码器的参数、读取温度传感器数据、或者访问板上的EEPROM,是构建完整系统不可或缺的“神经网络”。
C672x系列DSP通常集成了两个独立的I2C模块(I2C0和I2C1),这为系统设计带来了极大的灵活性。你可以用一个模块专用于控制本板的模拟前端(如ADC/DAC),另一个模块则用于与系统内其他主控制器(如另一个DSP或MCU)通信,甚至实现一个简单的人机接口。这种双模块设计避免了总线竞争,简化了软件架构。然而,要把这颗DSP的I2C用稳、用透,仅仅知道基本的读写操作是远远不够的,必须深入到寄存器层面,理解每一个配置位背后的硬件逻辑和电气特性。官方数据手册(Datasheet)和用户指南(User‘s Guide)是宝库,但信息分散且偏重描述,缺乏连贯的实战视角。本文就将结合手册要点与我的实际调试经验,为你拆解C672x I2C模块的配置精髓、寄存器详解以及那些手册上不会明写的“坑”。
2. C672x I2C模块架构与核心特性解析
2.1 模块整体框图与数据流
要驾驭一个外设,首先得在脑子里建立起它的硬件模型。C672x的I2C模块是一个高度集成化的数字逻辑单元,其核心架构可以理解为由几个协同工作的子模块构成:时钟生成、总线控制、数据移位、中断/事件管理以及引脚控制。
时钟生成是总线速度的基石。模块的输入时钟来源于DSP的SYSCLK2(外设时钟)。这个时钟频率可能很高(例如上百MHz),而I2C标准模式(100kbps)或快速模式(400kbps)所需的SCL频率要低得多。因此,模块内部首先通过一个可编程的预分频器(Prescaler, 由I2CPSC寄存器控制)对SYSCLK2进行初次降频,得到一个中间时钟IPSC_CLK。然后,这个IPSC_CLK再经过一个位时钟发生器(Bit Clock Generator),该发生器由I2CCLKH(时钟高电平分频)和I2CCLKL(时钟低电平分频)两个寄存器共同控制,最终产生符合I2C时序规范的SCL时钟。这种两级分频的结构提供了非常精细的时钟调节能力,你可以独立设置SCL高电平和低电平的持续时间,以适配总线上不同器件的时序要求,或者在存在较大布线电容时适当延长低电平时间以保证数据稳定。
数据流的核心是移位寄存器和缓冲器。当DSP作为发送方(Master-Transmitter或Slave-Transmitter)时,你将要发送的数据写入数据发送寄存器(I2CDXR)。该寄存器中的数据会被自动加载到发送移位寄存器(I2CXSR)中,在SCL时钟的同步下,一位一位地通过SDA线移出。反之,当接收数据时,SDA线上的串行数据先被移入接收移位寄存器(I2CRSR),凑满一个字节(8位)后,再自动转存到数据接收寄存器(I2CDRR)中,供CPU读取。这种双缓冲机制(DXR/XSR, RSR/DRR)是实现连续、流式数据传输而不产生间隙的关键。
控制逻辑是模块的大脑,主要由模式寄存器(I2CCMDR)和扩展模式寄存器(I2CEMDR)掌控。I2CCMDR决定了模块的基本工作模式:是作为主机(Master)还是从机(Slave)?是发送器(Transmitter)还是接收器(Receiver)?是否产生起始(START)和停止(STOP)条件?是否应答(ACK/NACK)?而I2CEMDR则控制一些高级功能,比如是否使能数字噪声滤波器(Digital Noise Filter)。这个滤波器对于工业环境等存在干扰的场景至关重要,它可以滤除宽度小于50ns的毛刺,防止误触发起始或停止条件,极大地提升了总线在恶劣电气环境下的鲁棒性。
中断与DMA事件是提高效率的关键。模块内部有丰富的状态标志,集中在中断状态寄存器(I2CSTR)中,例如数据发送完成(XRDY)、数据接收就绪(RRDY)、仲裁丢失(ARDY)、无应答(NACK)等。你可以通过中断使能寄存器(I2CIER)有选择地开启这些事件的中断。更高效的方式是配合DSP的dMAX(直接内存访问控制器)使用,将I2C配置为DMA事件源。这样,一旦发送缓冲空或接收缓冲满,就会自动触发DMA传输,在后台完成大批量数据的搬移,彻底解放CPU,使其能专注于核心算法运算。中断源寄存器(I2CSRC)则在多个中断事件同时发生时,帮助软件快速识别是哪一个具体事件触发了中断。
引脚控制寄存器组(I2CPFUNC, I2CPDIR等)提供了额外的灵活性。当I2C功能未被启用时,这两组引脚(I2Cx_SDA, I2Cx_SCL)可以被配置为通用的输入/输出(GPIO)使用。这在引脚资源紧张的系统中非常有用。但需要注意的是,一旦使能I2C功能,模块会自动接管引脚的方向控制,与GPIO配置寄存器无关。
2.2 关键特性与电气规范要点
C672x的I2C模块并非简单的IP核移植,它针对高性能DSP的应用场景做了一些特别的优化和限制,这些是配置前必须清楚的。
首先,它完全兼容Philips I2C规范v2.1,支持标准模式(0-100 kbps)和快速模式(高达400 kbps)。这意味着你可以放心地将其与市面上绝大多数I2C从设备连接。
其次,关于电气特性有一个至关重要的警告(CAUTION),这也是很多工程师初次使用容易栽跟头的地方:C672x的I2C引脚使用的是标准的±8mA驱动能力的LVCMOS缓冲器,而不是I2C规范中定义的具有“线与”特性的开漏缓冲器。这是一个本质区别。I2C总线的多主机仲裁和时钟拉伸等功能,依赖于开漏输出和外部上拉电阻实现的“线与”逻辑。C672x的推挽输出虽然驱动能力强,但无法与其他开漏输出的设备直接实现“线与”。
那怎么办?TI的建议是在PCB设计时,为SDA和SCL线串联一个小电阻(通常22Ω到100Ω)。这个电阻的作用是限流,并在多个设备输出冲突时(比如一个输出高,另一个输出低)起到一定的隔离作用,防止大电流损坏引脚。同时,总线两端依然需要连接上拉电阻(典型值3.3V系统用4.7kΩ)。这样,虽然DSP内部是推挽输出,但通过外部电阻的配合,在总线电平上仍然可以近似实现开漏的逻辑行为。忽视这一点,直接连接,很可能导致通信不稳定,甚至损坏引脚。
噪声滤波器是另一个亮点。在工业环境或长距离走线时,总线容易引入噪声。使能内置的数字噪声滤波器后,模块会对SDA和SCL输入信号进行采样,只有持续超过滤波器窗口时间(可滤除≤50ns噪声)的电平变化才会被识别为有效跳变。这相当于给总线加了一个软件可配置的“硬件RC滤波”,比单纯依靠外部电容滤波更精准可靠。
地址模式支持7位和10位两种。7位地址是最常用的,寻址空间为128个。10位地址扩展了寻址范围,但在实际应用中相对少见,需要主从设备双方都支持。
工作模式全面:支持主发送、主接收、从发送、从接收。这意味着C672x的I2C既可以主动发起通信控制其他芯片,也可以被动响应其他主机(如系统主控MCU)的访问,非常适合在复杂主从系统中作为协处理器。
3. 寄存器详解与实战配置指南
寄存器是软件与硬件对话的窗口。对C672x I2C的精准控制,完全依赖于对二十多个寄存器的理解。下面我们抛开枯燥的位域列表,从功能分组和实战配置的角度,深入解读关键寄存器。
3.1 时钟与速率配置寄存器组
这是配置的第一步,决定了通信的“心跳”频率。总线速度不匹配是通信失败的最常见原因之一。
I2CPSC(预分频寄存器): 这是第一级分频。假设你的SYSCLK2频率是SYSCLK2_FREQ,那么预分频后的时钟IPSC_CLK = SYSCLK2_FREQ / (I2CPSC + 1)。I2CPSC是一个8位寄存器,因此分频系数范围为1到256。它的主要作用是将高速的系统时钟降到一个中间频率,为后续的位时钟生成提供合适的时基。通常,我们会先根据目标SCL频率,反推出需要的IPSC_CLK,再计算I2CPSC的值。
I2CCLKH 与 I2CCLKL(时钟高低电平分频寄存器): 这是第二级分频,也是产生标准I2C波形(占空比)的关键。IPSC_CLK经过这两个寄存器分频后,分别产生SCL高电平时间和低电平时间。
- SCL高电平时间:
t_high = (I2CCLKH + 5) * (1 / IPSC_CLK) - SCL低电平时间:
t_low = (I2CCLKL + 5) * (1 / IPSC_CLK) - 因此,一个完整的SCL周期:
t_SCL = t_high + t_low - 最终的I2C总线速率:
I2C_BITRATE = 1 / t_SCL
这里的“+5”是模块内部的固定延迟因子,在计算时必须考虑进去。I2CCLKH和I2CCLKL也是8位寄存器。为了产生标准的I2C波形(通常高、低电平时间接近1:1),我们需要让t_high和t_low近似相等。这需要通过调整I2CPSC、I2CCLKH和I2CCLKL三个值来协同计算。
实战配置示例: 假设SYSCLK2 = 100 MHz, 目标I2C速率 = 400 kHz (快速模式)。
- 估算IPSC_CLK: 为了给高低电平分频留出足够精度,通常让IPSC_CLK在10-20MHz左右。我们暂定目标
t_SCL = 1/400kHz = 2.5 µs。如果希望t_high ≈ t_low ≈ 1.25µs,那么IPSC_CLK周期需要远小于1.25µs。取IPSC_CLK = 20 MHz(周期50ns),则分频数I2CPSC = 100MHz / 20MHz - 1 = 4。- 计算I2CCLKH和I2CCLKL:
- 对于高电平:
t_high = (I2CCLKH + 5) * 50ns = 1.25µs=>I2CCLKH = 1.25µs / 50ns - 5 = 25 - 5 = 20。- 对于低电平:
t_low = (I2CCLKL + 5) * 50ns = 1.25µs=>I2CCLKL = 20。- 验证与微调: 实际
t_SCL = (20+5+20+5)*50ns = 50*50ns = 2.5µs,速率正好400kHz。但需注意,数据手册的时序表对快速模式有要求,例如t_high和t_low均需≥0.6µs,我们的1.25µs满足要求。同时,要确保计算出的I2CCLKH和I2CCLKL值在0-255范围内。
3.2 地址与模式控制寄存器组
这组寄存器定义了“我是谁”以及“我要干什么”。
I2COAR(自身地址寄存器): 当DSP的I2C模块工作在从机模式时,这个寄存器定义了它在总线上的“门牌号”。主设备通过发送这个地址来寻址它。你可以配置为7位地址(写入高7位,最低位无效)或10位地址(通过模式寄存器I2CCMDR中的XA位选择)。重要提示:在7位地址模式下,写入I2COAR的地址值需要左移一位(即实际地址<<1)。这是因为I2C协议中,7位地址在发送时占据一个字节的高7位,最低位是读写方向位。模块硬件会自动处理方向位,但要求你提供左移后的地址值。
I2CSAR(从设备地址寄存器): 当DSP的I2C模块作为主机时,这个寄存器存放了你想要通信的从设备的地址。同样,对于7位地址模式,需要写入左移一位后的值。
I2CCMDR(模式寄存器): 这是I2C模块的“命令中心”,几乎所有的操作指令都通过写入此寄存器发起。关键位域包括:
- MST(主/从模式): 1=主机, 0=从机。
- TRX(发送/接收模式): 1=发送器(写操作), 0=接收器(读操作)。注意,此设置在主机和从机模式下含义不同。
- XA(扩展地址): 1=使用10位从机地址, 0=使用7位地址。
- RM(重复模式): 1=使能自动重复模式。在此模式下,当一次数据传输(字节数由I2CCNT设定)完成后,模块会自动重新开始一次传输(发送重复起始条件),而无需软件干预。这对于连续读取传感器数据非常高效。
- STB(起始字节模式): 用于与不支持I2C高速模式的微控制器兼容,不常用。
- IRS(I2C模块复位/使能): 这是最重要的位之一。在配置任何其他寄存器之前,必须先将此位清零以复位模块。完成所有配置后,再将此位置1以使能模块。任何对I2CCMDR、I2CPSC、I2CCLKH/L等寄存器的修改,都必须在IRS=0的情况下进行,修改完成后再置1生效。
- STP(停止条件): 在主机模式下,软件置位此位将在当前字节传输结束后产生一个停止条件。通常用于结束一次传输。
- STT(起始条件): 在主机模式下,软件置位此位将产生一个起始条件(或重复起始条件)。当总线空闲(BB位为0)且模块已使能(IRS=1)时,置位STT将启动一次主机传输序列。
- FDF(自由数据格式): 1=使能自由数据格式,此时不检查地址,直接进行数据收发,用于某些特殊协议。
- BC(位计数): 在主机模式下,指示当前正在传输的字节中已经移出了多少位。只读。
I2CEMDR(扩展模式寄存器): 主要控制数字噪声滤波器(NFE位)和输出驱动模式(但如前所述,驱动模式受硬件限制)。使能噪声滤波器(NFE=1)在噪声环境下是推荐做法。
3.3 数据与状态寄存器组
这是数据传输和状态监控的通道。
I2CDXR(数据发送寄存器): CPU将待发送的数据字节写入此寄存器。如果发送移位寄存器(I2CXSR)为空,数据会自动加载进去并开始发送。在重复模式或DMA模式下,需要连续向此寄存器写入数据。
I2CDRR(数据接收寄存器): 当接收移位寄存器(I2CRSR)收满一个字节后,数据会自动转存到此寄存器,并置位RRDY状态标志。CPU从此寄存器读取接收到的数据。
I2CCNT(数据计数寄存器): 这是一个非常实用的寄存器,用于在重复模式(RM=1)下定义一次传输过程中要发送或接收的字节数。当传输的字节数达到I2CCNT设定的值时,模块会自动停止(如果STP已设置)或产生中断。这简化了软件对传输长度的管理。
I2CSTR(中断状态寄存器): 实时反映模块的内部状态。关键状态位:
- BB(总线忙): 1表示I2C总线正被占用(SDA或SCL为低)。
- RSF(重复起始条件检测): 在从机模式下,检测到重复起始条件时置位。
- XRDY(发送就绪): 1表示I2CDXR为空,可以写入下一个待发送数据。
- RRDY(接收就绪): 1表示I2CDRR中有新数据可读。
- ARDY(寄存器访问就绪): 当对I2CCMDR的写入操作已完成(例如STT, STP命令已执行),或一次重复模式传输完成时置位。
- NACK(无应答): 作为主机时,发送完地址或数据后未收到从机的应答信号,此位置位。
- AL(仲裁丢失): 在多主机系统中,当本机在仲裁中失败时置位。
I2CIER(中断使能寄存器): 其位域与I2CSTR一一对应。将某位置1,则当I2CSTR中对应状态位置位时,会向CPU产生中断请求。你可以根据需要开启中断,例如开启XRDY和RRDY用于查询式传输,或开启ARDY、NACK、AL用于事件处理。
I2CSRC(中断源寄存器): 当产生中断时,读取此寄存器可以快速确定是哪个具体事件(XRDY, RRDY, ARDY等)触发了中断。在多个中断使能的情况下,这比轮询I2CSTR更高效。
3.4 引脚控制寄存器组(GPIO功能)
当I2C功能未启用时(通过PINMUX或其他系统配置将引脚功能设置为GPIO),这组寄存器用于控制引脚。
- I2CPFUNC(引脚功能寄存器): 决定引脚是用于I2C功能(0)还是GPIO功能(1)。
- I2CPDIR(引脚方向寄存器): 当为GPIO时,0=输入,1=输出。
- I2CPDOUT(引脚数据输出寄存器): 当为GPIO输出时,写入此寄存器控制输出电平。
- I2CPDIN(引脚数据输入寄存器): 当为GPIO输入时,读取此寄存器获取引脚电平。
- I2CPDSET/I2CPDCLR(引脚数据置位/清零寄存器): 提供原子操作,方便对GPIO输出位进行置1或清0,而不影响其他位。
重要提醒:一旦通过系统级引脚复用控制将引脚功能切换到I2C,模块将自动管理SDA和SCL的方向(开漏输出/输入),此时GPIO方向寄存器(I2CPDIR)不再起作用。试图在I2C通信过程中通过GPIO寄存器去操作这些引脚是无效且危险的。
4. 完整配置流程与代码实战
理解了寄存器,我们来看如何将它们串联起来,完成一次完整的I2C主设备读写操作。以下流程基于查询模式(非中断/DMA),适合初学者理解底层机制。
4.1 初始化配置步骤
- 引脚复用配置: 首先,需要通过系统控制模块(例如PINMUX寄存器)将对应的I2Cx_SDA和I2Cx_SCL引脚功能设置为I2C,而不是GPIO或其他功能。这是前提,否则后续所有I2C寄存器配置都不会生效。
- 复位并禁用I2C模块: 向I2CCMDR寄存器写入0,确保IRS位为0。这会复位整个I2C模块到默认状态。
- 配置时钟预分频器(I2CPSC): 根据你的SYSCLK2频率和目标I2C速率,计算并写入I2CPSC值。
- 配置时钟高低分频器(I2CCLKH, I2CCLKL): 根据计算出的值写入,以设定精确的SCL周期和占空比。
- 配置自身地址(I2COAR, 仅从机模式需要): 如果DSP可能作为从机被访问,在此设置它的7位或10位地址(注意左移)。
- 配置扩展模式(I2CEMDR): 通常使能数字噪声滤波器(NFE=1)。
- 使能I2C模块: 向I2CCMDR寄存器写入一个值,将IRS位置1,同时根据初始需求设置其他位(例如MST=1为主机模式)。此时模块已上电,但总线尚未启动。
4.2 主机发送(写)数据流程
假设我们要作为主机,向一个7位地址为0x50的EEPROM设备写入数据。
- 写入从机地址: 将目标从机地址左移一位后写入I2CSAR。例如,0x50 << 1 = 0xA0。注意,最低位是读写位,但写入I2CSAR时我们只关心地址部分,方向由后续操作决定。实际上,在启动传输后,模块会自动将I2CSAR中的值(0xA0)与方向位(0表示写)组合成第一个发送字节(0xA0 & 0xFE = 0xA0)。
- 设置数据计数(可选): 如果使用重复模式,将要发送的字节数(包括从机地址字节!)写入I2CCNT。例如,要发送3个数据字节,则I2CCNT = 4(1地址+3数据)。
- 配置模式寄存器并启动传输:
- 构建I2CCMDR命令字:设置MST=1(主机), TRX=1(发送), STT=1(产生起始条件), STP=1(在传输结束后产生停止条件)。如果不使用重复模式,STP=1是必要的。如果需要使用重复模式,则设置RM=1,并且STP位的行为取决于具体需求。
- 在确保总线空闲(通过读取I2CSTR的BB位确认)后,将上述命令字写入I2CCMDR。
- 等待并发送数据:
- 写入I2CCMDR后,等待ARDY位置位,表示起始条件已成功发出,模块就绪。
- 然后,轮询I2CSTR的XRDY位。当XRDY=1时,表示发送缓冲I2CDXR为空,可以写入第一个数据字节(即要写入EEPROM的数据)。
- 将数据写入I2CDXR。写入后,XRDY会清零,直到这个字节被移出发送缓冲。
- 重复“轮询XRDY -> 写入I2CDXR”的过程,直到所有数据发送完毕。
- 等待传输结束:
- 发送完最后一个数据字节后,等待ARDY再次置位。这表示STP命令已执行,停止条件已产生,整个传输序列结束。
- 在整个过程中,务必监控NACK和AL位。如果NACK置位,说明从机未应答,需要错误处理。如果AL置位,说明在多主机仲裁中失败。
4.3 主机接收(读)数据流程
假设我们要从地址0x50的EEPROM读取3个字节数据。
- 写入从机地址(写方向): 同样,将0xA0写入I2CSAR。
- 发送“伪写”以设置从机内部地址(很多存储器设备需要此步骤): 这个过程和主机发送流程完全一样,先发送起始条件+从机地址(写)+ 存储器内部地址。发送完成后不要产生停止条件。
- 发送重复起始条件(Repeated Start)并切换为读方向:
- 重新配置I2CCMDR: MST=1, TRX=0(接收), STT=1(产生重复起始条件), STP=1(读完后停止)。注意,此时从机地址寄存器I2CSAR需要更新吗?实际上,在重复起始后,模块会自动重新发送I2CSAR中的地址字节,但会将方向位置1(读)。所以,我们只需要确保I2CSAR中仍然是0xA0即可。
- 写入此I2CCMDR命令字。
- 等待并接收数据:
- 等待ARDY,表示重复起始条件已发出。
- 轮询I2CSTR的RRDY位。当RRDY=1时,表示接收缓冲I2CDRR中有新数据。
- 从I2CDRR中读取数据。关键点:在读取最后一个字节之前,软件需要通过对I2CCMDR的操作,告诉模块在接收到最后一个字节后不发送应答(NACK),并向从机发出停止条件。这通常通过在一个特定的时机(比如接收倒数第二个字节后)清除I2CCMDR中的ACK位来实现。具体操作需要仔细查阅数据手册中关于主机接收模式的描述。
- 等待传输结束: 读取完所有数据并产生停止条件后,等待ARDY置位。
4.4 示例代码片段(概念性)
以下是用C语言伪代码展示的主机发送单字节的流程,假设已定义好寄存器映射指针(如I2C0_REGS)。
// 1. 初始化配置 (假设SYSCLK2=100MHz, 目标400kHz) I2C0_REGS->I2CCMDR = 0x0000; // IRS=0, 复位模块 // 配置时钟, 使用前面计算的值 I2C0_REGS->I2CPSC = 4; // 预分频 I2C0_REGS->I2CCLKH = 20; // 高电平分频 I2C0_REGS->I2CCLKL = 20; // 低电平分频 I2C0_REGS->I2CEMDR |= 0x01; // 使能噪声滤波器 (NFE=1) // 2. 使能模块, 设置为主机模式 I2C0_REGS->I2CCMDR = 0x4000; // IRS=1, MST=1, 模块使能为主机 // 3. 准备发送数据到地址0x50的从机 uint16_t slaveAddr = 0x50 << 1; // 左移一位得到I2C格式地址 I2C0_REGS->I2CSAR = slaveAddr; // 设置从机地址 // 4. 启动传输 (发送起始条件+地址+一个数据字节+停止条件) // 构建命令: IRS=1, MST=1, TRX=1, STT=1, STP=1 uint16_t cmd = 0x4620; // 二进制: 0100 0110 0010 0000 // 位15-10: 保留 | IRS=1 | 保留 | MST=1 | TRX=1 | XA=0 | RM=0 | STB=0 // 位7-0: 保留 | STT=1 | 保留 | STP=1 | 保留 | BC位(只读) // 等待总线空闲 while (I2C0_REGS->I2CSTR & 0x8000); // 等待BB位为0 I2C0_REGS->I2CCMDR = cmd; // 写入命令,启动传输 // 5. 等待ARDY,表示起始条件已发出,可以发送数据了 while (!(I2C0_REGS->I2CSTR & 0x0004)); // 等待ARDY置位 // 6. 等待发送缓冲空,然后写入数据 while (!(I2C0_REGS->I2CSTR & 0x0008)); // 等待XRDY置位 I2C0_REGS->I2CDXR = 0xAB; // 发送数据字节 0xAB // 7. 等待传输完成 (ARDY再次置位,表示停止条件已产生) while (!(I2C0_REGS->I2CSTR & 0x0004)); // 8. 检查错误 if (I2C0_REGS->I2CSTR & 0x0010) { // NACK错误处理 } if (I2C0_REGS->I2CSTR & 0x0002) { // 仲裁丢失处理 }5. 高级应用与调试技巧
5.1 中断与DMA驱动配置
对于需要高效处理大量数据或实时响应的应用,查询模式会占用大量CPU资源。使用中断或DMA是必然选择。
中断驱动: 你需要初始化DSP的中断控制器,将I2C中断事件(如XRDY, RRDY, ARDY, NACK)映射到特定的中断线,并编写中断服务程序(ISR)。在ISR中,通过读取I2CSRC寄存器判断中断源,然后进行相应的数据处理(如填充I2CDXR或读取I2CDRR)。关键是要确保ISR执行时间尽可能短,避免丢失后续数据或总线超时。
DMA驱动: 这是最高效的方式,尤其适合音频流等大数据量传输。你需要配置dMAX控制器,将I2C的XRDY或RRDY事件作为DMA传输的触发源。例如,当I2CDXR空(XRDY事件)时,自动触发DMA将内存中的一个数据块搬运到I2CDXR;当I2CDRR满(RRDY事件)时,自动触发DMA将I2CDRR的数据搬运到内存。CPU只需要在传输开始前设置好DMA参数,传输完成后处理一个完成中断即可。这几乎实现了零CPU开销的I2C数据传输。
5.2 时序计算与电气匹配
手册中的时序表(Table 4-36, Table 4-37)是设计的金科玉律。在配置I2CCLKH和I2CCLKL时,计算出的t_high和t_low必须满足快速模式或标准模式下的最小值要求。同时,还要考虑总线电容Cb对上升时间tr的影响。公式tr = 20 + 0.1Cbns给出了在快速模式下,总线电容对上升时间的贡献。如果你的总线上挂载设备多、走线长,导致Cb接近400pF的最大值,那么上升时间可能会达到60ns,你需要确保这个值仍然小于300ns的最大限制。
上拉电阻的选择: 这是一个经典的权衡。电阻值小(如1kΩ),上升时间快,驱动能力强,但功耗大,低电平电流大。电阻值大(如10kΩ),功耗低,但上升时间慢,可能在高电容总线上无法满足时序。对于3.3V系统,在总线电容不大(<100pF)时,4.7kΩ是一个常用的折中选择。务必使用示波器测量SCL和SDA的实际波形,确保上升/下降沿干净,高低电平稳定。
串联电阻的必要性: 再次强调,由于C672x使用LVCMOS推挽输出,强烈建议在DSP的I2C引脚输出端串联一个22Ω-100Ω的小电阻。这能有效抑制信号振铃,并在总线冲突时保护DSP引脚。
5.3 常见问题排查与实战心得
通信完全无响应,SCL/SDA线始终为高:
- 检查引脚复用:这是最容易被忽略的一步。确认系统PINMUX配置已将对应引脚功能设置为I2C,而非GPIO。
- 检查模块使能:确认I2CCMDR中的IRS位已置1。
- 检查物理连接:测量上拉电阻是否焊接,电压是否为3.3V。用示波器查看发起起始条件时,SCL和SDA是否有下拉动作。
能发出起始条件,但地址无应答(NACK):
- 检查从机地址:确认写入I2CSAR的地址是否正确(是否左移了一位)。用逻辑分析仪抓取波形,看发出的地址字节是否与预期一致。
- 检查从机设备:确认从设备已上电,地址无误,且本身功能正常。
- 检查总线竞争:是否有其他设备拉低了总线?断开其他设备测试。
通信时好时坏,数据错误:
- 检查时序:用示波器测量SCL频率、高低电平时间、建立保持时间是否满足从设备的数据手册要求。重点检查
tsu(SDA-SCLH)(数据建立时间)和th(SDA-SCLL)(数据保持时间)。 - 检查噪声:使能I2CEMDR中的数字噪声滤波器(NFE=1)。检查PCB布局,I2C走线是否远离高频噪声源(如时钟线、开关电源)。
- 检查电源完整性:DSP和从设备的电源是否干净?在VDD和GND之间就近放置去耦电容。
- 检查时序:用示波器测量SCL频率、高低电平时间、建立保持时间是否满足从设备的数据手册要求。重点检查
从机模式下无法被寻址:
- 检查自身地址:确认I2COAR寄存器配置正确(地址左移)。
- 检查模块模式:确保I2CCMDR中的MST位为0(从机模式)。
- 检查总线拉高:确保总线上有上拉电阻,并且没有被持续拉低。
使用DMA时数据错位或丢失:
- 检查DMA触发对齐:确保DMA的传输宽度(8位/16位/32位)与I2C数据寄存器(8位)匹配。通常设置为8位传输。
- 检查DMA和I2C中断优先级:如果同时使用了I2C事件中断和DMA完成中断,要合理设置中断优先级,避免处理不及时导致缓冲区溢出或下溢。
- 核对DMA传输数量:I2CCNT寄存器设置的字节数需要与DMA配置的传输数量协调好。在重复模式下,I2CCNT计数到零会产生事件,可以用于触发DMA链的切换或中断。
个人心得:调试I2C,一个可靠的逻辑分析仪或带I2C解码功能的示波器是必不可少的。它能直观地展示起始、停止、地址、数据、ACK/NACK位,让你迅速定位问题是出在协议层还是物理层。另外,在软件中增加丰富的状态打印和错误计数器,记录每次通信的BB、ARDY、NACK、AL等状态,对于追踪间歇性故障非常有帮助。最后,对于C672x,牢记其I2C输出非纯开漏的特性,在设计PCB时就预留串联电阻的位置,能避免很多后续的麻烦。
