公司动态
MSPM0 I2C DMA触发与中断配置实战:释放CPU,提升嵌入式通信效率
1. 项目概述与核心价值如果你在嵌入式开发中用过I2C总线特别是需要高速、连续地读取传感器数据或写入大量配置参数肯定遇到过CPU被频繁中断“绑架”的窘境。每收发一个字节就产生一次中断CPU忙着进出中断服务程序真正的主循环逻辑反而没时间执行系统效率大打折扣。这正是DMA直接内存访问技术大显身手的地方。它就像一个专职的“数据搬运工”能在不打扰CPU的情况下自动完成外设比如I2C和内存之间的数据搬运。但要让这个“搬运工”知道什么时候该干活、干完活怎么通知你就需要一套精密的“触发与通知”机制。在TI的MSPM0系列微控制器上这套机制的核心就是DMA触发事件配置和中断寄存器管理。简单来说你需要告诉DMA控制器“当I2C的发送FIFO快空了MTXFIFOTRG或者接收FIFO有数据了SRXFIFOTRG你就自动去搬数据搬完了再通过MDMA_DONE_TX或SDMA_DONE_RX这样的中断告诉我一声。”这篇文章我就结合MSPM0 L系列的数据手册和实际调试经验为你彻底拆解I2C模块的DMA触发机制与中断寄存器配置。这不是照本宣科地翻译手册而是把手册里零散的寄存器描述、触发条件、中断标志串成一个你能直接抄作业、能理解背后逻辑的实战方案。无论你是想优化现有项目的I2C吞吐率还是在为新项目选型评估理解这套机制都能让你对MCU的资源调度有更深的掌控力。2. I2C DMA触发机制深度解析2.1 DMA触发的基本原理与架构在深入寄存器之前我们必须先建立对MSPM0 I2C DMA触发架构的宏观认识。这里的DMA触发本质上是一种事件驱动的数据传输。I2C模块内部会产生各种事件Event例如FIFO达到预设水位、一次传输完成等。这些事件可以作为“触发器”直接启动DMA通道进行数据传输完全无需CPU软件干预。MSPM0的I2C模块为DMA准备了两个专用的触发事件管理寄存器DMA_TRIG0和DMA_TRIG1。它们分别对应两个DMA通道Channel。你可以把每个DMA_TRIG寄存器看作一个“事件路由器”或“触发源选择器”。它的作用是将I2C内部产生的某个具体事件比如发送FIFO空、接收FIFO满映射到对应的DMA通道触发信号上。根据数据手册中的描述对应Figure 22-17每个DMA通道的触发源可以是多种条件之一Table 22-19列出了这些条件。对于I2C而言最常用、最实用的触发源就是FIFO状态事件主要包括MTXFIFOTRG: 主控制器Controller发送FIFO触发。通常配置为当发送FIFO中的数据量小于或等于某个阈值时触发意味着需要DMA尽快补充数据到FIFO以防发送中断。MRXFIFOTRG: 主控制器接收FIFO触发。通常配置为当接收FIFO中的数据量大于或等于某个阈值时触发意味着需要DMA尽快将数据从FIFO搬走以防数据溢出。STXFIFOTRG: 从设备Target发送FIFO触发。逻辑同MTXFIFOTRG但作用于从设备发送模式。SRXFIFOTRG: 从设备接收FIFO触发。逻辑同MRXFIFOTRG但作用于从设备接收模式。关键理解这里的“触发”是指启动一次DMA传输。例如将DMA_TRIG1配置为MTXFIFOTRG后每当I2C控制器的发送FIFO数据量低于阈值硬件会自动拉高DMA通道1的触发信号DMA控制器随即启动一次传输从指定内存地址搬运若干字节数据到I2C的CTXDATA寄存器即发送FIFO直到FIFO填满或DMA传输计数结束。2.2 触发事件配置的实战逻辑那么具体如何配置呢手册提到了需要参考Section 7.2.5事件寄存器配置和Section 7.1.3.2DMA触发事件工作原理。虽然我们手头没有这些章节但结合I2C章节的寄存器描述和通用DMA知识可以还原出完整的配置链条选择触发事件首先你需要决定哪个I2C事件去触发哪个DMA通道。例如你希望用DMA自动发送数据那么就应该将DMA_TRIG1假设对应DMA通道1的触发源配置为MTXFIFOTRG控制器发送FIFO触发。如果你希望用DMA自动接收数据则可以将DMA_TRIG0对应DMA通道0配置为MRXFIFOTRG。设置FIFO触发阈值触发事件何时发生这由FIFO控制寄存器CFIFOCTL和TFIFOCTL中的TXTRIG和RXTRIG字段决定。对于发送TXFIFOTXTRIG定义了“FIFO中的数据量小于等于N时”产生触发。例如设置TXTRIG 2意味着当发送FIFO中剩余数据小于等于2字节时MTXFIFOTRG事件激活触发DMA来补充数据。对于接收RXFIFORXTRIG定义了“FIFO中的数据量大于等于N时”产生触发。例如设置RXTRIG 6意味着当接收FIFO中累积数据达到6字节时MRXFIFOTRG事件激活触发DMA将数据搬走。这个阈值的设置是一门平衡艺术。设得太激进发送阈值设得高接收阈值设得低DMA触发频繁可能增加总线竞争。设得太保守则可能因为FIFO腾空或填满而导致I2C通信出现等待影响实时性。通常对于8字节深的FIFO发送阈值设为1或2接收阈值设为6或7是比较常见的选择为DMA响应留出一定时间窗口。配置DMA通道在DMA控制器模块中你需要配置对应通道Channel 1或0的源地址、目标地址、传输数据宽度应与I2C数据宽度一致通常是字节、传输次数等。最重要的是将通道的触发源选择为来自I2C模块的对应事件信号。启用事件到中断的映射可选除了触发DMA这些FIFO触发事件也可以配置为产生CPU中断。这通常用于在DMA传输的“头尾”进行干预比如在DMA传输开始前做一些准备工作或在传输完成后进行后续处理。这需要通过中断相关的寄存器如IMASK来配置。2.3 双通道配置实例与信号流分析让我们看一个典型的双通道配置例子这也是手册中提到的场景场景I2C控制器同时需要发送和接收大量数据。配置DMA通道1 (DMA_TRIG1)触发源配置为MTXFIFOTRG。用于自动填充发送FIFO。DMA通道0 (DMA_TRIG0)触发源配置为MRXFIFOTRG。用于自动清空接收FIFO。工作流程软件启动I2C传输写入目标地址和传输长度。初始时发送FIFO为空MTXFIFOTRG条件立即满足触发DMA通道1启动。DMA将第一批数据比如8字节从内存搬运到I2C的CTXDATA寄存器。I2C硬件开始发送数据。随着数据被逐个移出发送FIFO数据量减少当再次低于TXTRIG阈值时再次触发DMA通道1补充数据如此循环直到所有数据发送完毕。与此同时如果这是一次读操作I2C从总线上接收数据并存入接收FIFO。当接收FIFO中数据量达到RXTRIG阈值时MRXFIFOTRG事件激活触发DMA通道0启动。DMA将数据从I2C的CRXDATA寄存器搬运到内存。当整个DMA传输所有数据块完成时DMA控制器会向I2C模块反馈一个“完成”信号。根据配置这个完成信号可以来自控制器DMA或目标DMA。完成中断如图22-17所示当DMA通道1的状态变为完成done时会设置MDMA_DONE_TX和MDMA_DONE_RX中断标志这里需要根据实际传输方向理解可能图示是泛指。同理DMA通道2完成时会设置SDMA_DONE_TX和SDMA_DONE_RX中断标志。注意这里的Controller Interrupt”和“Target Interrupt”指的是中断信号的分类MDMA和SDMA可能分别对应主模式和从模式下的DMA完成中断具体需要查阅芯片数据手册的中断向量表。在中断服务程序ISR中你可以检查这些标志位得知DMA传输已结束进而进行后续处理如校验数据、启动下一轮操作等。避坑指南务必区分DMA触发事件如FIFOTRG和DMA完成中断如DMA_DONE。前者是“开始干活”的哨声由I2C模块发给DMA后者是“活干完了”的报告由DMA控制器或I2C模块在DMA传输完成后产生通知CPU。配置时容易混淆导致DMA不启动或无法收到完成通知。3. 中断寄存器配置详解与操作流程配置好DMA触发只是打通了自动传输的“任脉”而要顺畅地管理整个流程特别是处理传输完成、错误等情况就必须打通中断系统的“督脉”。MSPM0 I2C的中断系统设计得比较规整但寄存器数量较多需要理清层次。3.1 中断寄存器全景与功能划分I2C模块的中断寄存器主要分为三组分别服务于不同的中断输出线CPU_INT组偏移地址从0x1020到0x1048。这是最常用的一组用于产生连接到CPU核的常规中断。所有I2C事件传输完成、FIFO触发、错误等的中断都汇聚到这里。DMA_TRIG1组偏移地址从0x1050到0x1078。这组寄存器专门用于管理映射到DMA_TRIG1事件线的中断。从手册看它只包含四个FIFO触发事件CRXFIFOTRG, CTXFIFOTRG, TRXFIFOTRG, TTXFIFOTRG。这意味着你可以独立地为DMA触发事件配置中断使能、状态查询和清除而不影响CPU_INT线上的其他中断。DMA_TRIG0组偏移地址从0x1080到0x10A8。功能与DMA_TRIG1组完全对称服务于DMA_TRIG0事件线。每组中断寄存器都包含以下5个关键寄存器构成了一个完整的中断管理单元IIDX (Interrupt Index)中断索引寄存器。读取该寄存器可以直接获取当前最高优先级的、已使能的待处理中断的编号。同时读取操作会自动清除该中断在RIS和MIS中的标志位。这在采用轮询方式查询中断源时非常高效。IMASK (Interrupt Mask)中断掩码寄存器。每一位对应一个具体的中断源。写1使能解除屏蔽该中断写0禁用屏蔽。只有被IMASK使能的中断其状态才会反映到MIS寄存器并可能影响IIDX。RIS (Raw Interrupt Status)原始中断状态寄存器。无论IMASK如何设置只要硬件事件发生对应的RIS位就会被置1。它反映了所有中断源的“原始”状态常用于调试或特殊的轮询场景。MIS (Masked Interrupt Status)掩码后中断状态寄存器。该寄存器的值是RIS IMASK的结果。只有被IMASK使能的中断事件才会在这里显示为1。CPU通常查询MIS或IIDX来判断是否需要处理中断。ISET (Interrupt Set)/ICLR (Interrupt Clear)中断置位与清除寄存器。向ISET的某位写1可以软件模拟该中断事件的发生用于测试。向ICLR的某位写1则可以清除对应的RIS标志位从而也清除了MIS标志。这是清除中断状态的正确方式。3.2 关键中断标志位精讲以最常用的CPU_INT组为例其IMASK/RIS/MIS寄存器如0x1028,0x1030,0x1038的位定义涵盖了I2C所有可能的事件。我们需要重点关注与DMA协作相关的几个CRXDONE / CTXDONE (位0, 1)控制器接收/发送单字节完成。在非DMA模式下每收/发一个字节都会产生此中断。在DMA模式下通常禁用这些中断以避免频繁中断CPU。CRXFIFOTRG / CTXFIFOTRG (位2, 3)控制器接收/发送FIFO触发事件。这正是我们之前讨论的DMA触发源。在DMA模式下我们可能不在CPU_INT组使能它们而是将其路由到DMA_TRIGx组专门用于触发DMA。当然你也可以同时使能让它在触发DMA的同时也通知CPU。CDMA_DONE_TX / CDMA_DONE_RX (位11, 12)控制器DMA发送/接收完成中断。这是DMA传输完成的核心通知标志。当DMA通道完成所有数据传输后此位会被置1。必须在中断服务程序中读取ICLR寄存器相应位来清除它。TRXFIFOTRG / TTXFIFOTRG (位18, 19)目标从机接收/发送FIFO触发事件。当I2C模块工作在目标模式且使用DMA时其作用类似于控制器的FIFOTRG。TDMA_DONE_TX / TDMA_DONE_RX (位25, 26)目标DMA发送/接收完成中断。此外还有一些重要的状态和错误中断CNACK / ADRACK (位7及相关状态)地址或数据未收到应答NACK。一旦发生通常意味着通信失败需要软件干预。CARBLOST / TARBLOST (位10, 30)仲裁丢失。在多主机的I2C总线中如果两个主机同时发起传输可能会发生仲裁丢失。CRXFIFOFULL / CTXEMPTY (位4, 5)FIFO满/空。如果DMA响应不及时可能导致这些状态可作为系统流量监控的辅助标志。3.3 完整的中断配置与处理流程假设我们要配置一个使用DMA的I2C控制器发送任务并希望在发送完成后收到中断。以下是详细的步骤和代码思路步骤1初始化与DMA触发配置// 1. 配置I2C FIFO触发阈值 (假设使用8字节FIFO) // 当发送FIFO数据 2字节时触发DMA请求 I2C0-CFIFOCTL.TXTRIG 2; // 2. 将发送FIFO触发事件映射到DMA_TRIG1事件线 // 此配置通常在系统级事件互联模块中完成并非I2C寄存器本身。 // 假设有一个事件路由器寄存器将I2C的CTXFIFOTRG内部信号连接到DMA_TRIG1输出。 // 例如EVT-ROUTE.I2C0_CTXFIFOTRG DMA_CH1_TRIG; // 3. 配置DMA通道1 // - 源地址: 你的数据数组地址 (内存) // - 目标地址: (I2C0-CTXDATA) (I2C发送数据寄存器) // - 传输宽度: 8位 (字节) // - 传输次数: 待发送数据长度 // - 触发源: 选择为 I2C0_CTXFIFOTRG (即上一步路由的事件) // - 传输模式: 选择“单次请求-单次传输”或“单次请求-突发传输”根据需求定。 DMA_ConfigChannel(1, SRC_ADDR, (uint32_t)(I2C0-CTXDATA), DMA_SIZE_BYTE, data_len, TRIG_SRC_I2C0_TX, MODE_BURST);步骤2中断使能配置// 1. 在I2C模块中使能控制器DMA发送完成中断 (CPU_INT组) I2C0-IMASK | (1 11); // 使能 CDMA_DONE_TX 中断位 // 2. (可选) 如果你也希望FIFO触发事件能产生CPU中断用于监控可以同时使能 // I2C0-IMASK | (1 3); // 使能 CTXFIFOTRG 中断 // 3. 在NVIC嵌套向量中断控制器中使能I2C模块对应的全局中断线。 NVIC_EnableIRQ(I2C0_IRQn);步骤3启动传输// 1. 确保I2C控制器已正确初始化模式、速率、使能等。 // 2. 写入目标设备地址和传输方向到CSA寄存器。 I2C0-CSA.TADDR slave_addr; I2C0-CSA.DIR 0; // 0表示发送(写) // 3. 设置本次传输的总字节数到CCTR.CBLEN I2C0-CCTR.CBLEN data_len; // 4. 配置CCTR寄存器生成START并在传输结束后生成STOP然后启动传输。 I2C0-CCTR (1 2) | (1 1) | (1 0); // STOP1, START1, BURSTRUN1 // 5. DMA会在CTXFIFOTRG事件触发下自动开始搬运数据。 // 此时CPU可以去做其他任务。步骤4中断服务程序ISR处理void I2C0_IRQHandler(void) { // 1. 读取MIS或IIDX寄存器判断中断来源 uint32_t mis I2C0-MIS; // 2. 处理DMA发送完成中断 if (mis (1 11)) { // CDMA_DONE_TX // DMA传输已完成所有数据 // 可以进行后续操作例如检查CCTR.BUSY是否已变为0或准备下一次传输 // ... // 3. 清除中断标志位 (向ICLR对应位写1) I2C0-ICLR (1 11); } // 4. 处理其他中断如果有使能 if (mis (1 7)) { // CNACK // 处理NACK错误 handle_nack_error(); I2C0-ICLR (1 7); } // ... 处理其他中断 }3.4 EVT_MODE寄存器中断/事件模式选择还有一个至关重要的寄存器EVT_MODE(0x10E0)。它决定了三条中断/事件输出线CPU_INT, DMA_TRIG1, DMA_TRIG0的工作模式位[1:0] INT0_CFG: 对应CPU_INT线。位[3:2] INT1_CFG: 对应DMA_TRIG1线。位[5:4] EVT2_CFG: 对应DMA_TRIG0线。每2位字段可配置为00: 禁用。该事件线不工作。01:软件模式。中断标志RIS必须由软件写入ICLR寄存器来清除。这是CPU中断的典型模式。10:硬件模式。中断标志由硬件通常是另一个模块如DMA控制器自动清除。这正是DMA触发事件线的典型配置。当DMA_TRIGx线配置为硬件模式并且其触发事件如FIFOTRG发生时不仅会触发DMA而且在DMA控制器确认触发后硬件会自动清除对应的RIS标志无需软件干预。这保证了DMA触发事件的及时响应和自动管理。因此在配置DMA触发时通常需要// 将DMA_TRIG1和DMA_TRIG0事件线设置为硬件自动清除模式 I2C0-EVT_MODE (0x2 2) | (0x2 0); // INT1_CFG2, INT0_CFG2? 注意需要根据手册确认位域。 // 更可能的配置是CPU_INT线用软件模式DMA触发线用硬件模式。 // 假设复位后INT0_CFG1软件模式我们只需设置DMA相关线为硬件模式。 I2C0-EVT_MODE (0x2 4) | (0x2 2); // EVT2_CFG2 (DMA_TRIG0), INT1_CFG2 (DMA_TRIG1)4. 调试模式与寄存器访问要点4.1 仿真模式下的行为控制在进行调试时我们经常需要暂停CPU进入Halt模式来检查寄存器状态。但外设如I2C可能正在进行的通信会被打断导致数据丢失或总线挂死。MSPM0通过PDBGCTL (Peripheral Debug Control)寄存器 (0x1018) 提供了控制选项。FREE (位0): 自由运行控制。0: 当内核暂停Core Halted时外设也冻结。1: 忽略内核暂停状态外设继续运行。在调试实时通信时建议设置为1否则单步调试会导致I2C时序错乱。SOFT (位1): 软暂停边界控制仅在FREE0时有效。0: 外设立即停止即使可能导致状态损坏。1: 外设在到达一个“安全边界”如完成当前字节传输后才停止避免数据损坏。建议在开发初期可以将FREE设为1确保调试时I2C总线不被意外冻结。在排查特定问题时可以设置为SOFT模式以便在可控的点暂停观察。4.2 关键寄存器访问顺序与陷阱访问I2C寄存器尤其是控制寄存器时顺序和状态依赖非常重要使能顺序必须先通过PWREN寄存器使能模块电源再通过CCR.ACTIVE或TCTR.ACTIVE使能控制器或目标功能。顺序颠倒可能导致访问无效。FIFO状态寄存器CFIFOSR和TFIFOSR的注释明确提到“Note: this Register should only be read when BUSY is 0”。这意味着在I2C忙碌CSR.BUSY1或TSR显示忙碌时读取FIFO状态计数器可能得到不准确的值。在DMA传输过程中如果需要查询FIFO深度最好通过DMA本身的传输计数器来推算而非直接读此寄存器。中断清除清除中断标志的正确方法是向ICLR寄存器的对应位写1而不是向RIS或MIS写0。向ISET写1则是软件模拟中断用于测试。密钥保护一些关键寄存器如PWREN、RSTCTL有写保护密钥KEY字段。写入这些寄存器前必须先向KEY字段写入特定的魔术数字如PWREN的KEY是0x26RSTCTL的KEY是0xB1否则写入操作会被忽略。这是一个重要的安全特性防止代码跑飞意外修改关键设置。5. 常见问题与实战调试技巧5.1 DMA不触发或触发异常症状配置了DMA和I2C FIFO触发但DMA传输无法启动。排查思路确认事件路由首先检查系统级别的事件互联Event Router配置确保I2C的CTXFIFOTRG等内部事件信号确实连接到了你期望的DMA_TRIGx输入。这一步常常在芯片的SysConfig工具或初始化代码中完成容易被忽略。检查EVT_MODE确认对应的DMA_TRIGx线如INT1_CFG for DMA_TRIG1是否已使能并设置为正确的模式硬件模式通常为2。验证FIFO触发阈值检查CFIFOCTL.TXTRIG/RXTRIG的值是否合理。例如如果TXTRIG设为7触发条件为FIFO数据≤7而FIFO深度为8那么几乎总是满足触发条件可能导致DMA持续触发。通常设为1或2。检查DMA通道配置确认DMA通道的触发源选择正确并且通道已使能EN1。同时检查DMA的传输尺寸Item Size是否与I2C数据宽度8位匹配。使用中断辅助调试暂时在CPU_INT组使能CTXFIFOTRG中断并在其中断服务函数里设置一个标志或翻转一个GPIO。如果这个中断能正常产生说明I2C内部事件产生了问题可能出在事件路由或DMA配置。如果中断不产生则问题在I2C本身的FIFO配置或传输未启动。5.2 DMA传输完成中断不产生症状数据似乎传输完成了但预期的CDMA_DONE_TX/RX中断标志没置位。排查思路检查IMASK首先确认CDMA_DONE_TX/RX的中断是否已在IMASK寄存器中使能。检查传输长度与DMA配置确认I2C控制器设置的传输长度CCTR.CBLEN与DMA配置的传输次数Transfer Size是否一致。如果DMA传输次数小于I2C预期长度I2C会等待更多数据DMA完成中断可能不会触发因为DMA认为完成了但I2C没完成。反之如果DMA次数更多则DMA会尝试读取多余数据可能出错。检查CCTR配置确保CCTR.STOP位在最后一次传输中被置位。对于简单的单次读写通常在启动传输BURSTRUN1时同时设置START1和STOP1。如果STOP未置位I2C可能会等待后续数据不认为传输结束从而不产生完成状态。查询原始状态直接读取RIS寄存器查看CDMA_DONE_TX/RX的原始标志是否置位。如果RIS置位而MIS未置位问题在IMASK如果RIS都没置位则说明硬件未产生该事件。检查总线状态通过CSR寄存器检查是否有错误发生如ADRACK地址无应答或ARBLOST仲裁丢失。这些错误会导致传输中止可能不会正常到达DMA完成状态。5.3 FIFO溢出或下溢症状通信中出现数据丢失或乱码可能伴随CRXFIFOFULL或CTXEMPTY中断/标志。排查思路优化触发阈值这是最常见的原因。如果DMA响应速度跟不上I2C总线速度就会发生FIFO溢出接收时或下溢发送时。尝试调整FIFO触发阈值为DMA响应留出更多时间。例如将接收触发RXTRIG从7改为5更早触发DMA或将发送触发TXTRIG从1改为2更晚触发但需确保FIFO不会空。提高DMA优先级或总线时钟检查DMA通道的优先级是否被设置得太低被更高优先级的DMA或CPU问阻塞。也可以尝试提高系统或DMA总线时钟频率。使用更大数据块如果每次传输的数据量很小比如几个字节DMA的启动开销可能得不偿失。考虑累积更多数据再进行一次DMA传输或者直接使用中断模式。监控FIFO状态在调试阶段可以定期在安全时机读取CFIFOSR.RXFIFOCNT/TXFIFOCNT来观察FIFO的实际填充情况辅助判断阈值设置是否合理。5.4 调试技巧利用寄存器进行状态诊断当通信出现问题时不要盲目修改代码系统地检查以下寄存器能快速定位问题CBMON (0x1234)直接读取SCL和SDA引脚的电平状态。这是判断总线物理层是否正常上拉是否有效、是否被意外拉低的最直接方法。CSR (0x1218)BUSY控制器是否正在通信。IDLE控制器是否空闲。BUSBSY总线是否被占用其他主机在通信。ERR,ADRACK,DATACK最近的错误信息。ARBLOST是否丢失仲裁。STAT (0x814)RESETSTKY位可以指示模块自上次清除后是否发生过复位有助于判断程序是否意外复位了外设。通过IIDX快速定位中断源在中断服务函数中不要仅仅依赖预先使能的几个中断位来判断。可以先读取IIDX.STAT的值它直接告诉你当前最高优先级的待处理中断编号。对照手册中的中断向量表Table 22-30可以立即知道是哪个事件触发了中断这对于诊断意外中断非常高效。配置I2C DMA就像给通信流程安装了自动导航和物流系统。一旦调通CPU就从繁重的字节搬运中解放出来整个系统的实时性和能效比都会有质的提升。MSPM0的这套触发和中断机制设计得相当灵活虽然初期配置寄存器看起来有些繁琐但理解其“事件-触发-完成通知”的脉络后就能在各种复杂场景下游刃有余。最关键的是多动手测试用逻辑分析仪抓取SCL/SDA波形结合寄存器状态你就能清晰地看到DMA如何与I2C硬件协同数据如何如流水般自动传输。