Tiva C系列I2C μDMA突发传输原理与实战配置详解

发布时间:2026/7/23 1:19:26

Tiva C系列I2C μDMA突发传输原理与实战配置详解 1. 项目概述与核心价值在嵌入式系统开发中I2C总线因其简洁的硬件连接和灵活的通信方式成为了连接各类传感器、存储器和外设的“血管”。然而当数据吞吐量增大或系统实时性要求提高时传统的轮询或中断驱动数据传输方式往往会成为性能瓶颈大量消耗宝贵的CPU周期。这时硬件加速机制的价值就凸显出来了。本文将以德州仪器TI的Tiva™ C系列微控制器如TM4C129为蓝本深入剖析其I2C模块与μDMA控制器协同工作的内部机制。核心在于理解如何利用μDMA的突发BURST传输模式配合I2C模块内置的FIFO实现一种“设置后不管”的高效数据传输流水线。这对于从事电机控制、多传感器数据融合、工业物联网网关等高实时性、高数据吞吐量嵌入式应用开发的工程师来说是提升系统整体性能、优化CPU负载分配的关键技术。通过本文你将不仅了解寄存器如何配置更能洞悉dma_req和dma_sreq这两个关键硬件信号在后台如何“智能”调度数据流从而设计出更高效、更可靠的通信驱动。2. I2C与μDMA协同工作原理深度解析要驾驭这套机制首先得跳出单纯配置寄存器的层面从系统架构的角度理解各个组件扮演的角色及其交互关系。我们可以把整个数据传输过程想象成一个现代化的物流仓库系统。2.1 核心组件角色扮演I2C主模块Master相当于物流公司的“调度中心”。它负责发起通信、产生时钟信号SCL、发送目标地址从设备地址并控制整个数据传输的流程起始、停止、重复起始条件。Tx/Rx FIFO这是位于I2C模块内部的“临时周转仓库”。Tx FIFO用于缓存待发送的数据Rx FIFO用于缓存接收到的数据。其深度有限通常是8级或16级核心作用是平滑数据流解决CPU/内存与相对低速的I2C总线之间的速度不匹配问题。μDMA控制器这是不知疲倦的“自动化搬运机器人”。它的任务是在CPU初始化设定好后自动在系统内存SRAM和I2C的FIFO“仓库”之间搬运数据块完全解放CPU。I2CMBLEN寄存器这是给“搬运机器人”μDMA下达的“本次搬运任务单”指定了本次突发传输需要搬运的总字节数。dma_sreq 与 dma_req 信号这是FIFO“仓库”向“搬运机器人”μDMA发出的两种不同类型的“需求信号”。dma_sreq单次请求相当于仓库管理员喊一声“来搬一箱货”。只要FIFO非空对于Rx或非满对于Tx且达到了预设的触发水平就会产生此请求触发μDMA进行一次单次传输通常是一个数据单元如一个字节或一个字。dma_req突发请求相当于仓库管理员说“有一大批货到了快来连续搬运”。这是一个更高效的请求模式通常在满足特定条件如FIFO中的数据量达到一个较高的阈值默认常为4字节时发出允许μDMA在一次响应中连续进行多次数据传输最大化总线利用率。2.2 协同工作流程总览整个高效传输的链条始于软件的正确配置。首先你需要配置μDMA通道告诉它源地址内存、目标地址I2C FIFO数据寄存器和传输模式。然后配置I2C模块使能FIFO并设置触发水平。最关键的一步是在启动I2C传输前软件可以预先填充Tx FIFO对于发送或确保Rx FIFO为空对于接收。当你在I2C主控制状态寄存器I2CMCS中设置BURST位并启动传输后硬件逻辑便自动接管对于主发送Master TransmitI2C模块开始发送地址帧。与此同时Tx FIFO的填充状态被持续监控。一旦其数据量低于某个阈值便会向μDMA发出请求dma_sreq或dma_reqμDMA随即响应将内存中的数据块搬入Tx FIFO确保在I2C总线需要发送下一个数据字节时FIFO中总有数据可用避免总线等待。对于主接收Master ReceiveI2C模块接收到的数据会源源不断地存入Rx FIFO。当Rx FIFO中的数据量达到触发水平时便会向μDMA发出请求μDMA则及时将数据从Rx FIFO搬移到安全的内存缓冲区防止FIFO溢出导致数据丢失。这个过程完全由硬件并行处理CPU仅在初始化和传输完成中断时介入实现了极高的效率。注意一个常见的误解是认为使能BURST模式后μDMA会无视FIFO状态一次性搬完I2CMBLEN指定的所有数据。实际上μDMA的搬运节奏始终受FIFO的请求信号控制BURST模式只是允许μDMA在收到dma_req时进行连续搬运其总搬运量仍受I2CMBLEN限制。I2CMBCNT寄存器会递减计数归零后请求停止直到下一个BURST命令。3. 关键寄存器配置与信号触发逻辑详解理解了宏观流程我们深入到寄存器配置和硬件信号产生的微观逻辑。这是驱动开发中最具挑战性的部分配置不当极易导致数据传输不完整或硬件挂起。3.1 主模块发送Tx时的μDMA请求逻辑当主模块配置为发送且使能了BURST模式时请求信号的产生逻辑是性能调优的关键。// 假设配置步骤基于TivaWare库风格伪代码 // 1. 配置I2C主时钟、GPIO等略 // 2. 配置并启用μDMA通道设置传输大小为8位字节目标地址为I2C FIFO数据寄存器 // 3. 配置I2C FIFO控制寄存器 (I2CFIFOCTL) HWREG(I2C0_BASE I2C_O_FIFOCTL) (TXTRIG_1 I2C_FIFOCTL_TXTRIG_S) | // 设置Tx FIFO触发水平例如1 I2C_FIFOCTL_TXFIFOEN; // 使能Tx FIFO // 4. 设置突发传输长度 (I2CMBLEN) HWREG(I2C0_BASE I2C_O_MBLEN) 64; // 本次突发传输总共需要发送64字节 // 5. 可选但推荐在启动BURST前预先填充一部分数据到Tx FIFO // 这可以避免总线启动后因FIFO空而产生的初始延迟 for(int i0; i4; i) { HWREG(I2C0_BASE I2C_O_FIFODATA) tx_buffer[i]; } // 6. 写入从机地址设置R/S位为0发送 HWREG(I2C0_BASE I2C_O_MSA) (slave_addr 1) | 0x0; // 7. 写入控制命令启动带BURST的传输 // 假设使用START BURST (无STOP)命令码根据表21-5为 0x40 (0100 0000b) HWREG(I2C0_BASE I2C_O_MCS) I2C_MCS_BURST | I2C_MCS_START;此时硬件逻辑开始工作场景A如果I2CMBLEN 4并且Tx FIFO的填充水平低于设定的触发值例如我们设TXTRIG1表示FIFO非满时即可请求那么在使能BURST的瞬间如果FIFO是空的dma_req和dma_sreq会同时置位。dma_req的置位是因为满足了突发请求的条件长度4且FIFO未满。μDMA会优先响应突发请求开始连续向FIFO填充数据。场景B如果I2CMBLEN 4即使使能BURST硬件也不会产生dma_req因为不满足突发长度条件。此时仅当Tx FIFO非满且填充水平低于触发值时dma_sreq会置位触发μDMA进行单次传输。μDMA会通过单次请求一次一个字节或字地搬运直到累计搬运的字节数达到I2CMBLEN设定值。3.2 主模块接收Rx时的μDMA请求逻辑接收模式的逻辑与发送对称但方向相反。// 1. 配置I2C FIFO控制寄存器 (I2CFIFOCTL) 用于接收 HWREG(I2C0_BASE I2C_O_FIFOCTL) (RXTRIG_4 I2C_FIFOCTL_RXTRIG_S) | // 设置Rx FIFO触发水平例如4 I2C_FIFOCTL_RXFIFOEN; // 使能Rx FIFO // 2. 设置突发接收长度 (I2CMBLEN) HWREG(I2C0_BASE I2C_O_MBLEN) 128; // 准备接收128字节 // 3. 写入从机地址设置R/S位为1接收 HWREG(I2C0_BASE I2C_O_MSA) (slave_addr 1) | 0x1; // 4. 写入控制命令启动带BURST的接收 // 命令码需包含ACK1自动应答例如 0x44 (0100 0100b) HWREG(I2C0_BASE I2C_O_MCS) I2C_MCS_BURST | I2C_MCS_START | I2C_MCS_ACK;接收流程如下初始时Rx FIFO为空无请求。主I2C开始接收数据从机返回的字节被存入Rx FIFO。一旦Rx FIFO中有数据dma_sreq立即置位通知μDMA“有货可取”。如果Rx FIFO中累积的数据达到或超过4字节这是dma_req触发的常见阈值dma_req也会置位。μDMA将启动突发传输快速将数据块从FIFO搬移到内存。μDMA持续工作直到从Rx FIFO中累计取走的字节数等于I2CMBLEN中设定的值随后请求信号撤销传输暂停。3.3 从模块的μDMA支持从模块Slave同样可以利用μDMA和FIFO。其核心控制位在从控制状态寄存器I2CSCSR的TXFIFO和RXFIFO位。从发送当主设备请求数据时如果从设备的Tx FIFO未满且数据量低于触发水平会产生dma_req请求μDMA填充数据如果只是需要下一个字节则产生dma_sreq。从接收当主设备发送数据到来时存入从设备的Rx FIFO。一旦FIFO中有数据就会产生dma_sreq数据量超过触发水平则产生dma_req请求μDMA将数据搬走。重要实践建议数据手册中的Note明确指出应用程序不应在连续的事务中在I2CSDR从数据寄存器非FIFO模式和Tx FIFO之间切换使用反之亦然。这意味着一旦你决定在从设备上使用FIFODMA模式进行某个通信流程就应该在整个流程中保持一致混合使用可能会导致不可预测的数据错乱或状态机错误。在初始化从设备时就应明确其工作模式。4. 配置实战从零构建一个完整的BURST传输示例理论需要实践来巩固。让我们以一个具体的场景为例使用TM4C129的I2C0作为主设备以突发模式向一个I2C EEPROM如AT24C256连续写入128字节的数据。我们将使用μDMA通道0并采用Ping-Pong缓冲模式以实现连续无间断传输。4.1 系统初始化与外设使能首先必须正确启用相关外设的时钟这是所有操作的基础。#include stdint.h #include stdbool.h #include inc/hw_memmap.h #include inc/hw_types.h #include driverlib/sysctl.h #include driverlib/gpio.h #include driverlib/pin_map.h #include driverlib/i2c.h #include driverlib/udma.h void InitSystem(void) { // 1. 配置系统时钟假设运行在120MHz SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); // 2. 使能I2C0和GPIOB假设I2C0 SCLPB2, SDAPB3的外设时钟 SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); // 等待外设就绪是良好习惯避免访问未就绪的寄存器 while(!SysCtlPeripheralReady(SYSCTL_PERIPH_I2C0)); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)); // 3. 使能μDMA控制器时钟 SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA)); // 4. 配置GPIOB的引脚2和3为I2C功能 GPIOPinConfigure(GPIO_PB2_I2C0SCL); GPIOPinConfigure(GPIO_PB3_I2C0SDA); GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2); // 注意SDA引脚必须配置为开漏输出 GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3); }4.2 I2C主模块与FIFO配置接下来初始化I2C主模块配置通信速率并关键性地设置FIFO和突发传输参数。void InitI2C0MasterForBurst(void) { // 1. 初始化I2C为主模式主机使能 I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false); // false表示不启用高速模式 // 也可以直接配置寄存器更精细控制 // HWREG(I2C0_BASE I2C_O_MCR) 0x10; // Master Enable // 2. 配置I2C时钟目标100kHz系统时钟120MHz // TPR (SysClk / (2 * (SCL_LP SCL_HP) * SCL_CLK)) - 1 // 假设SCL_LP6, SCL_HP4, SCL_CLK100000 // TPR (120,000,000 / (2*10*100,000)) - 1 (120,000,000 / 2,000,000) -1 60 -1 59 I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false); // 或者使用库函数I2CMasterInitExpClk已计算TPR这里展示寄存器操作原理 // HWREG(I2C0_BASE I2C_O_MTPR) 59; // 3. 配置FIFO控制寄存器 (I2CFIFOCTL) // 使能Tx和Rx FIFO设置触发水平。 // TXTRIG: 发送FIFO触发水平。设为1表示FIFO非满时即可请求DMA更激进延迟小。 // RXTRIG: 接收FIFO触发水平。设为4表示FIFO中有4个或以上数据时可触发突发请求。 uint32_t fifoCtlValue 0; fifoCtlValue | (1 I2C_FIFOCTL_TXTRIG_S); // TXTRIG 1 fifoCtlValue | (4 I2C_FIFOCTL_RXTRIG_S); // RXTRIG 4 fifoCtlValue | I2C_FIFOCTL_TXFIFOEN; // 使能Tx FIFO fifoCtlValue | I2C_FIFOCTL_RXFIFOEN; // 使能Rx FIFO HWREG(I2C0_BASE I2C_O_FIFOCTL) fifoCtlValue; // 4. 设置突发传输长度寄存器 (I2CMBLEN) // 本次示例我们计划传输128字节。这个值决定了DMA传输的总量。 HWREG(I2C0_BASE I2C_O_MBLEN) 128; // 5. 清除可能存在的旧中断标志并配置中断如果需要 I2CMasterIntClear(I2C0_BASE); // 如果需要使用中断来检测传输完成或错误在此使能相应中断 // HWREG(I2C0_BASE I2C_O_MIMR) I2C_MIMR_IM; // 使能主中断 }4.3 μDMA通道配置Ping-Pong模式这是实现高效连续传输的核心。Ping-Pong模式使用两个缓冲区Ping和Pong当DMA在操作其中一个缓冲区时CPU可以准备或处理另一个缓冲区的数据实现并行。// 定义数据缓冲区 #define BURST_SIZE 128 uint8_t g_ui8PingBuffer[BURST_SIZE]; uint8_t g_ui8PongBuffer[BURST_SIZE]; volatile bool g_bPingBufferReady true; volatile bool g_bPongBufferReady true; void InitDMAForI2C0Tx(void) { // 1. 启用μDMA控制器并设置控制表基地址 uDMAEnable(); uDMAControlBaseSet(sDMAControlTable[0]); // sDMAControlTable需在链接脚本中分配 // 2. 配置DMA通道属性假设使用通道0 // 设置通道为I2C0 Tx请求源高优先级 uDMAChannelAssign(UDMA_CH0_I2C0TX); uDMAChannelAttributeEnable(UDMA_CH0_I2C0TX, UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_USEBURST); // 3. 配置Ping缓冲区的传输控制结构 // 传输模式基本模式完成一次传输后停止等待下次触发 // 源地址Ping缓冲区地址内存 // 目的地址I2C0 FIFO数据寄存器 (I2C0_BASE I2C_O_FIFODATA) // 传输大小BURST_SIZE (128) 个数据项 // 数据项大小8位字节 // 源地址增量每次传输后源地址1从内存数组顺序读取 // 目的地址增量0目的地址是固定寄存器不递增 uDMAChannelControlSet(UDMA_CH0_I2C0TX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_128); // 仲裁大小设为128与传输总数一致 uDMAChannelTransferSet(UDMA_CH0_I2C0TX | UDMA_PRI_SELECT, UDMA_MODE_BASIC, g_ui8PingBuffer, (void*)(I2C0_BASE I2C_O_FIFODATA), BURST_SIZE); // 4. 配置Pong缓冲区的传输控制结构使用备用通道控制结构 // 模式、目的地址、数据项属性与Ping相同仅源地址不同 uDMAChannelControlSet(UDMA_CH0_I2C0TX | UDMA_ALT_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_128); uDMAChannelTransferSet(UDMA_CH0_I2C0TX | UDMA_ALT_SELECT, UDMA_MODE_BASIC, g_ui8PongBuffer, (void*)(I2C0_BASE I2C_O_FIFODATA), BURST_SIZE); // 5. 使能DMA通道 uDMAChannelEnable(UDMA_CH0_I2C0TX); }4.4 启动传输与主循环处理配置完成后启动第一次传输并在主循环或中断中处理缓冲区切换。void StartI2CBurstTransfer(uint8_t slaveAddr) { // 1. 准备Ping缓冲区数据例如要写入EEPROM的数据 for(int i0; iBURST_SIZE; i) { g_ui8PingBuffer[i] i; // 示例数据 } g_bPingBufferReady true; g_bPongBufferReady false; // Pong缓冲区尚未准备好 // 2. 写入从机地址假设EEPROM写地址 // EEPROM写操作通常需要先发送设备地址含写位和内存地址高位、低位。 // 这里简化假设slaveAddr已是包含写位的完整地址。 // 注意在BURST模式下对I2CMSA的写入可能影响状态机需严格按流程。 // 通常先写地址再配置DMA和FIFO最后发STARTBURST命令。 I2CMasterSlaveAddrSet(I2C0_BASE, slaveAddr, false); // false表示写 // 3. 确保Tx FIFO为空或预填充少量数据可选但可减少初始延迟 // 由于我们使用DMA自动填充这里可以不预填。 // 4. 写入控制命令启动BURST传输。 // 命令字 START1, STOP0, RUN0, BURST1, ACKX (0), HS0, QCMD0 // 根据表21-5对于Idle状态STARTBURST的命令码为 0x40 (0100 0000b) HWREG(I2C0_BASE I2C_O_MCS) I2C_MCS_START | I2C_MCS_BURST; // 注意此时RUN位为0BURST位为1符合表中“0 1 0 X 0 0 1 0”的编码。 // 传输由硬件自动进行。DMA会根据FIFO的请求信号自动填充数据。 // I2C模块会发送START条件、从机地址然后连续发送FIFO中的数据。 } // DMA传输完成中断服务程序需在启动文件中关联 void DMA_IRQHandler(void) { uint32_t ui32Status uDMAIntStatus(); uDMAIntClear(ui32Status); if(ui32Status UDMA_INT_CH0) { // 通道0中断 // 检查是Ping还是Pong传输完成 uint32_t ui32Mode uDMAChannelModeGet(UDMA_CH0_I2C0TX); // 实际中应检查通道控制结构的状态位这里简化处理 static bool bIsPingActive true; if(bIsPingActive) { // Ping传输完成 // 1. 可以处理Ping缓冲区数据如果这是接收 // 2. 重新准备Ping缓冲区数据对于下一次发送 PrepareNextData(g_ui8PingBuffer, BURST_SIZE); g_bPingBufferReady true; // 3. 如果Pong缓冲区已就绪立即切换到Pong传输 if(g_bPongBufferReady) { uDMAChannelTransferSet(UDMA_CH0_I2C0TX | UDMA_PRI_SELECT, UDMA_MODE_BASIC, g_ui8PingBuffer, (void*)(I2C0_BASE I2C_O_FIFODATA), BURST_SIZE); // 注意在BURST模式下I2C传输可能仍在进行。 // 只需重新武装DMA通道当FIFO再次需要数据时DMA请求会自动触发。 uDMAChannelEnable(UDMA_CH0_I2C0TX); // 重新使能通道准备下一次传输 } bIsPingActive false; } else { // Pong传输完成处理逻辑类似 PrepareNextData(g_ui8PongBuffer, BURST_SIZE); g_bPongBufferReady true; if(g_bPingBufferReady) { uDMAChannelTransferSet(UDMA_CH0_I2C0TX | UDMA_ALT_SELECT, UDMA_MODE_BASIC, g_ui8PongBuffer, (void*)(I2C0_BASE I2C_O_FIFODATA), BURST_SIZE); uDMAChannelEnable(UDMA_CH0_I2C0TX); } bIsPingActive true; } // 注意对于发送需要在所有数据由I2CMBLEN定义发送完毕后由I2C模块产生完成中断或状态位变化。 // 此处DMA完成仅表示它搬完了指定数量的数据到FIFO不代表I2C总线已发送完毕。 } }在主循环中你需要监控I2C主状态寄存器I2CMCS的BUSY位或等待I2C中断以确定整个BURST传输何时结束然后发送STOP条件。void PollForTransferComplete(void) { // 等待I2C主控制器繁忙结束 while(HWREG(I2C0_BASE I2C_O_MCS) I2C_MCS_BUSY) { // 可以在此处添加超时处理 } // 检查错误位 if(HWREG(I2C0_BASE I2C_O_MCS) I2C_MCS_ERROR) { // 处理错误地址无应答(ADRACK)或数据无应答(DATACK) HandleI2CError(); } else { // 传输成功发送STOP条件结束本次通信 // 在BURST模式下发送STOP的命令码需要参考表21-5。 // 对于Master Transmit状态STOP命令是 0x04 (0000 0100b) HWREG(I2C0_BASE I2C_O_MCS) I2C_MCS_STOP; } }5. 常见问题排查与调试技巧实录即便理解了原理和配置步骤在实际调试中依然会遇到各种问题。以下是我在多个项目中总结出的常见坑点及其解决方案。5.1 传输挂起BUSY位永不清除现象启动BURST传输后I2CMCS寄存器的BUSY位一直为1程序卡死。排查思路检查从设备地址和应答这是最常见的原因。使用逻辑分析仪或示波器抓取I2C总线波形确认START条件后发出的第一个字节地址字节读写位是否正确以及从设备是否返回了ACK低电平。如果从设备无应答主设备会置位ERROR和ADRACK位并可能挂起。确保I2CMSA寄存器写入的值正确7位地址左移1位最低位为R/W。检查时钟配置不正确的I2CMTPR值会导致SCL时钟频率超出从设备支持的范围。计算你的TPR值并用示波器测量SCL实际频率。确保其符合标准模式100kHz、快速模式400kHz或你设定的模式。检查FIFO和DMA配置确认I2CFIFOCTL寄存器中的TXFIFOEN或RXFIFOEN已使能。确认I2CMBLEN寄存器设置了一个大于0的值。确认μDMA通道已正确使能并且控制结构中的传输大小ARB_SIZE与I2CMBLEN匹配或为其整数倍。关键点在BURST模式下I2CMCS的RUN位应为0BURST位为1。如果错误地将RUN置1硬件可能进入未定义状态。仔细对照表21-5检查你写入I2CMCS的命令码。检查总线冲突与上拉电阻I2C总线需要上拉电阻通常4.7kΩ。如果电阻值过大或缺失信号无法可靠拉高会导致通信失败。检查SDA和SCL线是否有对地短路或与其它信号线短路。5.2 DMA传输未触发或数据不完整现象I2C通信似乎启动了但数据没有发送出去或者只发送了一部分。排查思路确认DMA请求信号dma_req和dma_sreq是内部信号难以直接测量。但可以通过间接方式验证在I2CMCS寄存器中ACTDMATX和ACTDMARX位示DMA传输是否活跃。监控这些位。​I2CFIFOSTATUS寄存器可以读出Tx/Rx FIFO的当前填充水平。在发送时如果FIFO一直为空且DMA不活跃说明DMA请求可能未产生。检查FIFO触发水平TXTRIG和RXTRIG设置得太高可能会延迟DMA请求。对于发送尝试将TXTRIG设为最小值如1。这能确保FIFO一有空位就请求DMA减少总线空闲。验证DMA通道配置源地址和目标地址是否正确目标地址必须是I2CxFIFODATA寄存器。数据传输大小8位、16位、32位是否与I2C数据宽度匹配I2C FIFO数据寄存器通常是8位访问。DMA传输模式是否正确对于一次性BURST传输BASIC模式即可。对于Ping-Pong需要使用PINGPONG模式或手动切换PRIMARY和ALTERNATE控制结构。检查I2CMBLEN与DMA传输次数的关系I2CMBLEN定义了I2C模块期望通过FIFO传输的总字节数。DMA的传输次数ARB_SIZE的循环应至少满足这个数量。如果DMA配置的传输次数少于I2CMBLENI2C模块会在FIFO空后等待更多数据导致超时或挂起。5.3 中断与状态管理混乱现象程序进入了错误的中断服务程序或者无法准确判断传输何时结束。解决方案清晰区分中断源I2C完成、DMA完成、错误中断要分开处理。仔细配置I2CMIMR主中断屏蔽和I2CSIMR从中断屏蔽寄存器只使能你需要的中断。在中断服务程序ISR开始立即读取I2CMRIS原始中断状态或I2CMMIS屏蔽后中断状态来识别具体的中断源。BURST传输完成的判断在BURST模式下传输完成的标志不是简单的BUSY位变低。你需要结合以下条件BUSY位为0。I2CMBCNT主突发计数寄存器递减到0。可能还需要检查ACTDMATX/ACTDMARX位是否为0确认DMA已停止。 在查询法中轮询这些条件在中断法中可以使能DMARISDMA完成中断或I2C的特定中断如STOP条件中断如果配置了。及时清除中断标志在退出ISR前必须向I2CMICR主中断清除或I2CSICR从中断清除寄存器的相应位写入1来清除中断标志否则会持续触发中断。5.4 性能优化与权衡FIFO触发水平的权衡较低的触发水平如TXTRIG1可以减少总线等待时间提高实时性但会频繁触发DMA请求尤其是dma_sreq增加总线仲裁开销。较高的触发水平如TXTRIG6有利于触发dma_req进行突发传输效率更高但可能导致总线在传输开始时有短暂空闲。需要根据具体应用的数据流特性进行测试和调整。I2CMBLEN的设置此寄存器值决定了单次BURST命令传输的数据量上限。设置过小需要频繁发起新的BURST命令增加软件开销设置过大如果传输中途出错需要重传的数据量也大。通常将其设置为DMA缓冲区大小或一次事务处理的数据包大小。DMA优先级与仲裁大小在有多路DMA竞争的系统总线时提高I2C相关DMA通道的优先级可以保证数据传输的实时性。ARB_SIZE仲裁大小定义了DMA在一次dma_req响应中连续传输的最大数据项数将其设置为与I2CMBLEN相同或为其约数可以最大化突发传输效率。调试此类复杂外设交互逻辑分析仪是必不可少的工具。不仅要抓取I2C总线上的SDA/SCL信号如果MCU支持还应尝试利用其调试模块如ITM实时打印关键寄存器的值如I2CMCS、I2CFIFOSTATUS、I2CMBCNT这能让你清晰地看到硬件状态机的每一步变化从而快速定位问题根源。记住配置顺序很重要先初始化DMA和控制结构再配置I2C FIFO和I2CMBLEN最后写入从机地址和发送BURST启动命令。

相关新闻