
1. 项目概述与DMA核心价值在嵌入式实时控制系统的开发中尤其是在处理电机控制、数字电源、高频数据采集这类对时序和效率要求极其苛刻的场景时CPU的每一滴算力都显得弥足珍贵。如果让CPU亲自去搬运ADC采样得到的大量数据或者频繁地在内存与串行外设接口SPI之间拷贝数据无异于让一位高级工程师去干快递员的活不仅大材小用更会严重拖慢整个系统的响应速度。这时直接存储器访问DMA模块的价值就凸显出来了。你可以把DMA想象成系统内部一个高度专业、不知疲倦的“数据搬运工”。它独立于CPU核心拥有自己的控制逻辑和地址生成单元。当外设比如ADC转换完成或软件产生一个“有货要搬”的信号事件触发时DMA控制器就会自动启动按照你预先设定好的“搬运清单”即寄存器配置精准地从源地址读取数据再写入目标地址整个过程无需CPU插手。CPU只需要在初始化时写好这份“清单”并在搬运完成后处理一下中断如果需要其余时间可以完全专注于执行控制算法等核心任务。对于德州仪器TI的C2000系列微控制器如TMS320F2837xS其DMA模块的功能尤为强大。它不仅支持高效的内存到内存、外设到内存的数据传输更在多核C28xCLA架构中扮演了关键角色是实现核间高效、无干扰通信的基石。理解并熟练配置其寄存器是从“能用”到“用好”C2000的必经之路。本文将从最基础的寄存器位定义讲起结合官方示例代码带你一步步拆解DMA的工作机制并最终实现一个实用的多核间通过共享外设触发DMA的通信案例。2. DMA寄存器全景与核心概念解析在深入每个寄存器之前我们必须先建立几个核心概念这有助于理解后续所有配置的意图。C2000的DMA模块通常包含多个独立的通道例如6个每个通道都有一套完全独立的寄存器组来控制其行为。数据传输过程被组织为三个层级传输Transfer、突发Burst和字Word。一个完整的传输由若干次突发构成而一次突发则包含若干个字的连续搬运。这种层级结构非常灵活。例如在ADC采样场景中你可以配置一次传输Transfer对应一整个采样帧比如256个点而一次突发Burst则对应ADC结果寄存器被填满时产生的一个事件比如每次产生8个采样值。当DMA完成一个字的搬运后源地址和目标地址会根据SRC_BURST_STEP和DST_BURST_STEP寄存器进行更新通常是1或2完成一次突发后地址则会根据SRC_TRANSFER_STEP和DST_TRANSFER_STEP更新当整个传输完成如果使能了地址环绕Wrap功能地址还可能根据SRC_WRAP_STEP等寄存器进行大幅度的跳转这常用于循环缓冲区的实现。所有通道共享少数几个全局控制寄存器而每个通道则拥有多达二十几个专属寄存器。为了方便管理TI的软件库如DriverLib和寄存器定义头文件会将这些寄存器组织成清晰的结构体。理解这个结构是手动配置或调试的基础。2.1 全局控制寄存器指挥官全局寄存器控制着整个DMA模块的顶层行为它们位于一个固定的基地址例如0x0000_1000。虽然数量不多但每一个都至关重要。DMACTRL (DMA Control Register): 这是模块的总开关和复位控制。HARDRESET (位0): 硬复位位。向此位写1会复位整个DMA模块立即中止所有正在进行的数据传输并将所有寄存器恢复为默认值。这相当于给整个DMA部门拉闸断电再重启是一种非常粗暴但彻底的复位方式。手册特别强调写此位后需要插入一个空操作指令如NOP等待一个周期再访问其他DMA寄存器以确保复位生效。PRIORITYRESET (位1): 优先级复位位。在多通道环境下DMA内部有一个轮询调度状态机。向此位写1会重置这个状态机让服务从第一个使能的通道重新开始。如果通道1被设置为高优先级则复位后从通道2开始。这个复位不会中止当前正在进行的突发传输会等待其完成行为相对“温和”。DEBUGCTRL (Debug Control Register): 主要用于仿真调试。FREE (位15): 仿真运行控制位。当你在CCS中设置断点暂停CPU时这个位决定了DMA的行为。设为0DMA会在完成当前读写操作后停止设为1DMA则无视仿真暂停继续运行。在调试涉及DMA的实时数据流时这个位的设置需要格外小心否则你可能永远抓不到“正在传输”的瞬间。PRIORITYCTRL1 (Priority Control 1 Register): 用于配置通道1的优先级。CH1PRIORITY (位0): 通道1优先级选择。默认为0表示通道1与其他通道平等参与轮询。设置为1时通道1拥有最高优先级只要其有触发事件就会立即中断当前服务的低优先级通道抢占DMA总线进行传输。关键点修改此优先级必须在所有通道均被禁用的情况下进行并且在修改后、重新使能通道前必须执行一次优先级复位写PRIORITYRESET位。PRIORITYSTAT (Priority Status Register): 用于监视DMA内部状态。ACTIVESTS (位[2:0]): 指示当前正在执行传输的通道编号1-6。值为0表示没有通道活跃。ACTIVESTS_SHADOW (位[6:4]): 当通道1为高优先级时此影子寄存器用于保存被通道1中断的那个低优先级通道的编号。当通道1的服务完成后DMA会依据此影子寄存器的值恢复被中断通道的服务。这是一个用于实现优先级抢占和恢复的内部机制对软件透明但在调试复杂抢占场景时查看此寄存器有助于理解DMA的内部调度序列。2.2 通道控制寄存器每个搬运工的工单每个DMA通道都有一套完整的寄存器用于定义其具体的搬运任务。这套寄存器可以大致分为四类模式与控制类、尺寸与计数类、地址与步进类、地址环绕类。MODE寄存器定义了通道的工作模式。PERINTE (位8): 外设事件触发使能。这是DMA自动工作的关键必须置1DMA才会响应外设如SPI发送完成、ADC序列转换结束或软件强制触发的事件。PERINTSEL (位[4:0]):注意在F2837x等较新器件中这个位域是“遗产”位实际的外设触发源选择是通过另一组独立的DMACHSRCSELn寄存器完成的。通常这里只需简单地写入通道号本身例如通道1就写1。真正的触发源映射需要查阅芯片的《技术参考手册》。CHINTMODE (位9): 通道中断生成模式。0在一次传输开始时产生中断1在一次传输完成时产生中断。绝大多数应用场景我们选择1在数据就绪后再通知CPU。CHINTE (位15): 通道中断使能。需要产生中断时置1。DATASIZE (位14): 数据大小。0每次搬运16位半字1每次搬运32位字。重要无论此位如何设置其他所有涉及长度、步进的寄存器如BURST_SIZE,SRC_BURST_STEP其单位都是16位字。如果选择32位模式那么地址步进值需要相应调整例如想每次地址1个32位字则需要设置步进为2。ONESHOT (位10): 单次触发模式。如果置1一个外设事件触发会导致DMA完成整个传输即搬完TRANSFER_SIZE个突发。如果为0则一个触发只引起一次突发传输。对于需要与每个外设事件严格同步的场景如每个PWM周期更新一次比较寄存器常用ONESHOT模式。CONTINUOUS (位11): 连续模式。置1后当一次传输完成TRANSFER_COUNT减到0DMA会自动用影子寄存器的值重新加载所有活动寄存器并等待下一个触发从而实现循环传输。这在实现“乒乓”缓冲区或持续数据流时非常有用。CONTROL寄存器是通道的实时控制与状态中心。RUN (位0): 运行位。写1使能通道通道状态RUNSTS位会随之置1。这是启动DMA通道的“启动按钮”。HALT (位1): 暂停位。写1会暂停通道但保持当前所有寄存器状态。可用于临时挂起传输。SOFTRESET (位2): 通道软复位。写1会复位该通道到默认状态停止计数器清零但不会像硬复位那样影响其他通道。同样操作后需要NOP等待。PERINTFRC (位3): 软件强制触发位。即使没有外设事件向此位写1也能手动启动一次DMA传输非常便于测试。PERINTFLG (位8): 外设事件触发标志位。当触发事件到来或软件强制时此位由硬件置1。当DMA开始处理第一个突发时此位自动清零。你可以通过查询此位来了解是否有待处理的触发。TRANSFERSTS (位11) BURSTSTS (位12): 传输状态和突发状态标志。分别指示通道是否正处于一次传输或一次突发中。RUNSTS (位13): 运行状态标志。为1表示通道已使能正在等待或进行传输。尺寸与计数寄存器定义了搬运的“量”。BURST_SIZE: 定义一个突发包含多少个字16位。实际突发大小 设定值 1。例如想一次突发搬8个字则写入7。BURST_COUNT: 只读寄存器显示当前突发中还剩多少个字待搬运。TRANSFER_SIZE: 定义一次传输包含多少个突发。实际传输大小 设定值 1。TRANSFER_COUNT: 只读寄存器显示当前传输中还剩多少个突发待完成。地址与步进寄存器定义了搬运的“路径”。SRC_BEG_ADDR_SHADOW/DST_BEG_ADDR_SHADOW:影子开始地址寄存器。这是你在DMA传输过程中也可以安全写入的配置寄存器。当一次新的传输启动时这里的值会被加载到对应的“活动”寄存器中。SRC_ADDR_SHADOW/DST_ADDR_SHADOW:影子当前地址寄存器。同样可安全更新传输开始时加载到活动地址。SRC_BEG_ADDR_ACTIVE/DST_BEG_ADDR_ACTIVE:活动开始地址寄存器。只读反映当前传输周期使用的起始地址。SRC_ADDR_ACTIVE/DST_ADDR_ACTIVE:活动当前地址寄存器。只读实时显示DMA当前正在读/写的地址。这是调试时观察数据流的关键窗口。SRC_BURST_STEP/DST_BURST_STEP:突发内步进。每搬运一个字后源/目标地址的增量可正可负。例如从连续内存区域读取通常设为1或232位模式。SRC_TRANSFER_STEP/DST_TRANSFER_STEP:传输间步进。每完成一个突发后源/目标地址的增量。用于在内存中跳转到下一个数据块。地址环绕寄存器实现了复杂的缓冲区管理。SRC_WRAP_SIZE/DST_WRAP_SIZE:环绕大小。定义在多少次突发后地址需要“绕回”到起始点。实际环绕前突发数 设定值 1。如果想禁用环绕将此值设得比TRANSFER_SIZE大即可。SRC_WRAP_COUNT/DST_WRAP_COUNT:环绕计数器。只读显示当前距离下一次环绕还有多少个突发。SRC_WRAP_STEP/DST_WRAP_STEP:环绕步进。当环绕发生时不是简单地跳回BEG_ADDR而是在此地址基础上加上WRAP_STEP的值。这是实现“乒乓缓冲区”或“多缓冲区”的精华所在。例如你可以设置一个大小为N的缓冲区WRAP_SIZE设为N-1WRAP_STEP设为N。这样DMA会在填满第一个缓冲区后自动将起始地址跳到第二个缓冲区的开头实现自动切换。实操心得寄存器配置的顺序逻辑配置DMA通道时务必遵循一个安全的顺序先配置所有参数寄存器模式、尺寸、地址、步进等最后再置位CONTROL.RUN来启动通道。在修改任何配置参数前应先通过CONTROL.HALT暂停通道或通过CONTROL.SOFTRESET复位通道。对于SRC_BEG_ADDR_SHADOW这类影子寄存器虽然可以在传输中更新但为了逻辑清晰建议也在通道停止时修改。乱序操作可能导致DMA行为异常甚至引发总线错误。3. 从理论到实践DMA工程实例深度剖析TI的C2000Ware软件包提供了丰富的DMA示例这些示例是学习的最佳起点。我们选取两个最具代表性的例子进行拆解看看寄存器配置是如何转化为具体功能的。3.1 实例一GSRAM间数据传输 (dma_ex1_gsram_transfer)这个例子展示了DMA最基础的功能内存到内存的数据搬运。它使用一个DMA通道将数据从RAMGS0中的一个缓冲区搬运到RAMGS1中的另一个缓冲区。核心配置解析地址设置:SRC_BEG_ADDR_SHADOW/SRC_ADDR_SHADOW: 设置为源缓冲区位于RAMGS0的起始地址。DST_BEG_ADDR_SHADOW/DST_ADDR_SHADOW: 设置为目标缓冲区位于RAMGS1的起始地址。尺寸与步进设置:BURST_SIZE 7: 表示一个突发传输8个16位字71。TRANSFER_SIZE 15: 表示一次完整传输包含16个突发151。因此总数据量 8字/突发 * 16突发 128字。SRC_BURST_STEP/DST_BURST_STEP 1: 每搬运一个字源和目标地址都1指向下一个字。SRC_TRANSFER_STEP/DST_TRANSFER_STEP 8: 每完成一个突发8个字源和目标地址在突发内步进累积的基础上再8。但在这个简单线性搬运的例子中由于突发内步进已经让地址连续移动传输间步进通常设为0。这里设为8可能是个示例特例或者是为了演示功能。通常线性搬运时传输间步进设为0。模式与控制:MODE.CHINTE 1: 使能通道中断。MODE.CHINTMODE 1: 传输完成时产生中断。MODE.PERINTE 1: 使能外设事件触发本例中由软件强制模拟。MODE.ONESHOT 1? 根据描述示例是通过反复写PERINTFRC来触发直到完成16次突发。这意味着它可能没有设置ONESHOT1而是采用“一次触发对应一次突发”的模式由主循环软件控制触发16次。如果设置了ONESHOT1且TRANSFER_SIZE15那么一次软件触发就能完成全部128字的搬运。CONTROL.RUN 1: 使能通道等待触发。工作流程主程序初始化源数据缓冲区 (sData)。按上述配置初始化DMA通道。进入循环连续16次向CONTROL.PERINTFRC位写1手动触发DMA突发。DMA每次被触发搬运一个突发8个字。搬运完16个突发共128字后TRANSFER_COUNT减为0触发DMA中断。在中断服务程序ISR中检查目标缓冲区 (rData) 的数据是否正确并置位完成标志。注意事项软件触发与硬件触发的区别此例使用软件强制触发 (PERINTFRC) 来模拟外设事件。在实际应用中硬件触发源如ADC、SPI、ePWM会在特定时刻自动置起PERINTFLG标志。软件触发常用于测试或手动启动传输。需要注意的是如果PERINTFLG标志已经为1即有一个触发未处理此时再来一个新的硬件触发会导致溢出标志OVRFLG置位。软件写PERINTFRC则不会引起溢出它只在PERINTFLG为0时才能将其置1。3.2 实例二三多核共享外设触发 (dma_ex1_shared_periph_cpu1/2)这个例子展示了C2000多核系统中DMA的高级应用一个核CPU2控制的外设如SPI产生的事件去触发另一个核CPU1上的DMA传输。这对于双核协同工作、负载分离至关重要。场景设定CPU2: 拥有一个定时器和一个SPI外设作为主设备。定时器周期性中断在中断服务程序中CPU2启动一次SPI传输。CPU1: 配置了一个DMA通道其触发源被映射到CPU2所拥有的那个SPI的发送事件例如SPITXINT。同时CPU1还控制着一个ePWM模块。目标CPU2的定时器定时触发SPI通信SPI的发送完成事件自动触发CPU1的DMA。DMA则将预设的数据搬运到CPU1的ePWM模块的比较寄存器CMPA中从而动态改变PWM的占空比。最终在CPU1的GPIO引脚上可以用示波器观察到受DMA更新的PWM波形。关键配置与核间协作触发源映射关键: 这是多核DMA的核心。在C28x_DUAL器件中外设中断源是分配到具体CPU的。默认情况下CPU2的SPI中断不会直接触发CPU1的DMA。需要通过DMACHSRCSELn寄存器属于DMA_CLA_SRC_SEL_REGS组进行交叉触发映射。你需要将CPU1上某个DMA通道的触发源选择为来自CPU2的SPI发送中断。这个过程通常由TI的SysConfig图形化工具完成它会自动生成正确的配置代码。手动配置则需要仔细查阅芯片数据手册的“Interrupt”和“DMA”章节找到正确的映射关系位域。CPU1的DMA配置:MODE.PERINTE 1: 使能外设触发。MODE.PERINTSEL: 按惯例写入通道号但实际触发源由上述DMACHSRCSELn决定。MODE.ONESHOT 1: 很可能设置为1这样一次SPI发送完成事件就能让DMA完成整个传输例如更新一个CMPA值只需要一次突发搬运一个字。DST_BEG_ADDR_SHADOW: 目标地址设置为ePWM1的CMPA寄存器地址。这是一个外设寄存器地址而非内存地址。DMA可以访问整个内存映射空间包括外设寄存器帧。BURST_SIZE 0: 一次突发只搬运1个字01。TRANSFER_SIZE 0: 一次传输只包含1个突发01。所以一次触发就搬运一个字到CMPA。CONTROL.RUN 1: 使能DMA通道等待来自CPU2的SPI事件。CPU2的软件流程:配置定时器和SPI。在定时器中断中向SPI数据寄存器写入数据启动SPI发送。SPI硬件自动发送数据发送完成后产生中断事件。这个事件通过芯片内部的交叉触发网络送到了CPU1的DMA通道。执行顺序建议: 示例注释中提到建议先运行CPU1的代码初始化DMA并使其进入等待状态然后再运行CPU2的代码开始定时触发。这样可以确保DMA就绪在先避免第一个触发事件丢失。这个例子的精妙之处在于它实现了时间解耦和功能解耦。CPU2只负责按节奏产生“触发信号”通过SPI它完全不知道也不关心CPU1用这个信号具体做了什么。CPU1则专注于根据触发信号更新PWM它不需要主动去查询或请求数据。DMA作为无声的桥梁以极低的延迟和CPU开销将两个核的动作精确地同步起来。这种模式非常适用于主从核架构例如CPU2处理通信和调度CPU1专精于高速实时控制。4. 寄存器配置实战指南与避坑要点理解了原理和示例现在我们来梳理一套完整的、可复用的DMA通道配置流程并分享一些从实践中总结出来的“坑”与技巧。4.1 标准配置流程以下是一个通用的DMA通道初始化函数应遵循的步骤void DMA_Channel_Config(uint16_t ch) { // 步骤0确保通道被禁用或复位 DmaRegs.CH[ch].CONTROL.bit.RUN 0; // 确保RUN位为0 // 或者使用软复位更彻底 DmaRegs.CH[ch].CONTROL.bit.SOFTRESET 1; asm( NOP); // 等待一个周期确保复位生效 // 步骤1配置工作模式 (MODE寄存器) DmaRegs.CH[ch].MODE.bit.PERINTE 1; // 使能外设触发 DmaRegs.CH[ch].MODE.bit.PERINTSEL ch; // 按惯例设为本通道号实际源由DMACHSRCSELn决定 DmaRegs.CH[ch].MODE.bit.CHINTE 1; // 使能中断如果需要 DmaRegs.CH[ch].MODE.bit.CHINTMODE 1; // 传输完成产生中断 DmaRegs.CH[ch].MODE.bit.ONESHOT 0; // 根据需求选择0单次突发/触发1单次传输/触发 DmaRegs.CH[ch].MODE.bit.CONTINUOUS 0; // 根据需求选择0单次传输1连续循环 DmaRegs.CH[ch].MODE.bit.DATASIZE 0; // 016-bit, 132-bit // 步骤2配置传输尺寸 (BURST_SIZE, TRANSFER_SIZE) DmaRegs.CH[ch].BURST_SIZE.bit.BURSTSIZE BURST_LEN - 1; // 突发字数 DmaRegs.CH[ch].TRANSFER_SIZE.bit.TRANSFERSIZE TRANSFER_BURSTS - 1; // 传输包含的突发数 // 步骤3配置地址与步进 // 3.1 配置源地址相关 DmaRegs.CH[ch].SRC_BEG_ADDR_SHADOW (uint32_t)src_buffer; // 影子起始地址 DmaRegs.CH[ch].SRC_ADDR_SHADOW (uint32_t)src_buffer; // 影子当前地址 DmaRegs.CH[ch].SRC_BURST_STEP.bit.SRCBURSTSTEP src_burst_step; // 突发内地址增量通常为1或2 DmaRegs.CH[ch].SRC_TRANSFER_STEP.bit.SRCTRANSFERSTEP src_transfer_step; // 传输间地址增量 // 3.2 配置目标地址相关 DmaRegs.CH[ch].DST_BEG_ADDR_SHADOW (uint32_t)dst_buffer; DmaRegs.CH[ch].DST_ADDR_SHADOW (uint32_t)dst_buffer; DmaRegs.CH[ch].DST_BURST_STEP.bit.DSTBURSTSTEP dst_burst_step; DmaRegs.CH[ch].DST_TRANSFER_STEP.bit.DSTTRANSFERSTEP dst_transfer_step; // 步骤4配置地址环绕如果需要例如乒乓缓冲区 if(wrap_enable) { DmaRegs.CH[ch].SRC_WRAP_SIZE.bit.WRAPSIZE WRAP_SIZE - 1; DmaRegs.CH[ch].SRC_WRAP_STEP.bit.WRAPSTEP src_wrap_step; // 环绕后起始地址的偏移 // 同样配置 DST_WRAP_SIZE 和 DST_WRAP_STEP } else { // 禁用环绕将WRAPSIZE设置为大于TRANSFERSIZE的值 DmaRegs.CH[ch].SRC_WRAP_SIZE.bit.WRAPSIZE 0xFFFF; // 最大值 DmaRegs.CH[ch].DST_WRAP_SIZE.bit.WRAPSIZE 0xFFFF; } // 步骤5关键配置外设触发源选择 (DMACHSRCSELn) // 这是多核或复杂外设触发的关键需要根据具体外设和CPU映射来设置。 // 例如将通道ch的触发源选择为ADCINT1: // DmaClaSrcSelRegs.DMACHSRCSEL1.bit.CH1 0; // 假设CH1对应ADCINT1需查表确认 // 此部分代码高度依赖具体器件和连接通常由SysConfig生成。 // 步骤6清除任何可能存在的旧标志位 DmaRegs.CH[ch].CONTROL.bit.PERINTCLR 1; // 清除未决的触发标志 DmaRegs.CH[ch].CONTROL.bit.ERRCLR 1; // 清除溢出错误标志 // 步骤7使能通道等待触发 DmaRegs.CH[ch].CONTROL.bit.RUN 1; // 步骤8如果需要使能对应的CPU中断PIE组和中断线 // PieCtrlRegs.PIEIERx.bit.INTyx 1; // 使能PIE组内中断 // IER | M_INTx; // 使能CPU级中断 // EINT; // 全局开中断 }4.2 常见问题排查与调试技巧即使按照流程配置DMA有时也会“沉默”或不按预期工作。以下是一个排查清单和调试思路现象可能原因排查步骤与解决方法DMA完全不启动1. 通道未使能 (RUN位不为1)。2. 外设触发未使能 (PERINTE位为0)。3. 触发源映射错误 (DMACHSRCSELn配置错误)。4. 外设本身未产生中断事件。1. 检查CONTROL.RUNSTS位确认是否为1。2. 检查MODE.PERINTE位。3.重点检查DMACHSRCSELn寄存器配置对照数据手册的“Interrupt Mux”表格确认物理中断源是否正确映射到了DMA通道。4. 检查外设配置确认其中断是否已使能并正确产生。可以先用CPU中断测试该外设事件是否正常。DMA只搬运一次后停止1.CONTINUOUS模式未使能且ONESHOT1时一次触发完成整个传输后停止。2. 触发事件未持续产生。1. 检查MODE.CONTINUOUS和MODE.ONESHOT的设置是否符合预期。如果需要循环搬运应设置CONTINUOUS1。2. 检查外设是否周期性产生触发事件。对于软件触发检查触发代码是否在循环中。数据搬运到了错误地址1. 源/目标地址寄存器 (SHADOW) 配置错误。2. 步进寄存器 (BURST_STEP,TRANSFER_STEP) 配置错误特别是32位模式时。3. 地址环绕配置有误。1. 在调试器中查看SRC_ADDR_ACTIVE和DST_ADDR_ACTIVE观察其变化是否符合预期。2. 确认DATASIZE设置。若为32位模式地址步进应以16位字为单位。想地址4字节1个32位字步进应设为2。3. 检查WRAP_SIZE和WRAP_STEP的计算。确保环绕逻辑符合缓冲区设计。DMA中断未触发1. 通道中断未使能 (CHINTE0)。2. CPU级或PIE级中断未使能。3. 中断标志未清除或中断被屏蔽。4.CHINTMODE设置与预期不符期望结束时中断却设成了开始时。1. 检查MODE.CHINTE和MODE.CHINTMODE。2. 检查PIE配置 (PIEIER,PIEIFR) 和CPU的IER、IFR寄存器。3. 在DMA ISR中需要手动清除DMA通道的中断标志向DMAINTFLG寄存器的对应位写1并应答PIE (PieCtrlRegs.PIEACK)。多核系统中对端CPU的DMA无响应1. 触发源未正确映射到对端CPU的DMA通道。2. 对端CPU的DMA通道未正确初始化或未使能。3. 核间内存共享区域未正确配置或存在一致性问题。1.这是最常见原因。双核对DMACHSRCSELn寄存器的配置必须一致且正确。确保触发源是从“属”CPU映射到了“主”CPU的DMA通道。2. 确认对端CPU的代码已运行且DMARUN位已置1。3. 如果涉及共享RAM数据确保双核对该内存区域的访问权限已配置通过CSM或MPU并且考虑缓存一致性问题必要时使用CACHE操作或UNCACHED内存段。高级调试技巧使用CCS的寄存器视图和内存浏览器实时监控CONTROL寄存器中的状态位 (RUNSTS,TRANSFERSTS,BURSTSTS,PERINTFLG)以及ACTIVE地址寄存器的变化可以直观看到DMA是否在工作、处于哪个阶段。利用软件强制触发 (PERINTFRC)在硬件触发逻辑复杂时先使用软件触发测试DMA配置是否正确。如果能正常搬运则问题可能出在触发源映射或外设本身。关注时序与同步在多核或DMA与CPU共享总线/内存的场景如果DMA访问的内存区域被CPU频繁访问可能导致总线竞争影响实时性。合理规划内存布局将DMA缓冲区放在冲突少的RAM块如GSRAM或使用CPU缓存锁定技术。理解EALLOW保护DMA的配置寄存器大多受EALLOW保护。在写这些寄存器前需要执行EALLOW指令写完后执行EDIS指令。TI的DriverLib函数内部已经处理了这一点但如果是直接操作寄存器务必注意。5. 进阶应用构建一个双核乒乓缓冲区数据流让我们综合运用以上知识设计一个更复杂的实战场景CPU1进行高速ADC采样通过DMA将数据存入一个乒乓缓冲区。CPU2通过另一个DMA通道在缓冲区半满时将数据搬移到外部存储器或通过通信接口发送出去。系统设计内存规划在共享的GSRAM中定义两个缓冲区BufferA[256]和BufferB[256]。CPU1的ADC-DMA配置配置ADC序列每完成256次转换产生一个中断DMA触发源。配置DMA通道1为连续模式 (CONTINUOUS1)单次触发模式 (ONESHOT1)TRANSFER_SIZE255(256次突发)BURST_SIZE0(每次突发1个字即ADC一个结果)。设置SRC_BEG_ADDR_SHADOW为ADC结果寄存器地址。设置DST_BEG_ADDR_SHADOW初始指向BufferA。关键配置地址环绕。DST_WRAP_SIZE 255(256次突发后环绕)DST_WRAP_STEP 256(环绕后目标起始地址跳转256个字即从BufferA切换到BufferB)。使能传输完成中断。在中断中无需搬运数据仅设置一个标志通知CPU2哪个缓冲区已满。CPU2的搬运-DMA配置配置一个由CPU1软件标志触发可通过IPC中断或共享变量的DMA通道2。源地址设置为已满的缓冲区BufferA或BufferB目标地址为外部存储器接口或SPI发送缓冲区。配置合适的突发和传输大小。CPU1在ADC-DMA中断中通过核间通信IPC触发CPU2的DMA开始搬运另一个缓冲区。优势ADC采样是连续、硬实时的DMA保证了极低的延迟和CPU零开销。数据处理和传输由CPU2并行完成实现了高效的流水线操作。乒乓缓冲区避免了数据竞争确保了数据的完整性。这个案例体现了DMA在复杂实时系统中的核心价值它不仅是数据搬运工更是构建高效、确定性系统架构的关键组件。通过精细的寄存器配置你可以让DMA按照精确的“剧本”自动完成复杂的数据流管理将CPU解放出来去处理真正需要智能决策的任务。