TMS320F2838x DMA通道优先级配置与高优先级模式实战解析

📅 2026/7/19 14:49:40 👁️ 阅读次数 📝 编程学习
TMS320F2838x DMA通道优先级配置与高优先级模式实战解析

1. 项目概述:为什么DMA优先级配置是嵌入式开发者的必修课

在嵌入式系统开发,尤其是基于TMS320F2838x这类高性能实时微控制器的项目中,直接内存访问(DMA)模块的性能直接决定了整个系统的实时性和效率。很多工程师在初次接触DMA时,往往只关注如何启动一次数据传输,却忽略了其内部调度机制——特别是通道优先级配置。这就像只学会了开车,却不懂交通规则,在复杂的多任务道路上迟早会“堵车”甚至“撞车”。我见过不少项目,ADC采样数据偶尔丢失,或者SPI通信在特定时序下出现错位,排查到最后,根源往往出在DMA通道的优先级竞争上。

TMS320F2838x的DMA控制器提供了6个独立的通道,它们可以并行响应来自ADC、SPI、ePWM等不同外设的传输请求。如果没有合理的优先级策略,当多个外设同时或近乎同时产生DMA请求时,控制器该如何响应?是让它们“先到先得”,还是让更关键的数据流“插队”?通道1高优先级模式就是TI为开发者提供的一把“尚方宝剑”,它允许你将最关键的数据流(比如高速ADC的采样结果)固定在最高优先级,确保其传输的确定性。理解并熟练配置这套机制,是从“能让DMA跑起来”到“能让DMA在复杂系统中稳定、高效地跑起来”的关键跨越。本文将结合手册细节和实际工程经验,为你彻底拆解TMS320F2838x DMA的优先级逻辑与核心寄存器配置,让你不仅知其然,更知其所以然。

2. DMA通道优先级机制深度解析

2.1 默认轮询模式与通道1高优先级模式对比

在深入高优先级模式之前,我们必须先理解DMA的默认行为。TMS320F2838x的DMA控制器在默认情况下,所有6个通道(CH1-CH6)具有相同的优先级。当多个通道同时有待处理的传输请求时,控制器采用一种轮询(Round-Robin)算法进行调度。

你可以把轮询调度想象成一个旋转的餐桌。服务员(DMA控制器)按照固定的顺序(CH2 -> CH3 -> CH4 -> CH5 -> CH6 -> CH2...)依次询问每个座位(通道)是否需要上菜(数据传输)。每个通道在一次轮询周期中,只能完成一次“突发传输”。这种方式的优点是公平,每个通道都能获得服务机会,不会出现某个通道长期“饿死”的情况。但在实时性要求极高的系统中,这种公平恰恰是问题所在。如果一个低速的UART打印任务(CH3)正在传输一个大文件,而此时高速ADC(CH1)的采样缓冲区即将满溢并触发DMA请求,ADC通道也必须排队等待,这可能导致采样数据丢失。

为了解决这个问题,TMS320F2838x引入了通道1高优先级模式。在此模式下,通道1(CH1)被赋予了绝对的优先权,凌驾于其他所有通道(CH2-CH6)之上。CH2-CH6之间仍然保持轮询调度。这就好比在旋转餐桌上设置了一个“VIP专座”(CH1),服务员必须优先服务VIP的需求,之后才继续按顺序服务其他普通座位。

注意:一个关键的限制是,高优先级模式与ONESHOT模式不能同时在通道1上使用。ONESHOT模式是指每个外设事件触发会完成整个传输(所有突发),而非仅一个突发。如果你为CH1配置了高优先级,同时又使能了ONESHOT,其行为是未定义的,可能导致不可预料的错误。其他通道(CH2-CH6)则不受此限制,它们可以在CH1高优先级模式下正常使用ONESHOT。

2.2 高优先级模式下的抢占与恢复机制详解

手册中给出的例子非常经典,清晰地展示了抢占过程。假设当前系统配置为通道1高优先级模式,且通道1、4、5被使能。此时,通道4正在执行其突发传输。

  1. 中断触发:在CH4的传输尚未完成时,CH1和CH5各自的外设(例如ADC和SPI)同时产生了事件,触发了DMA请求。
  2. 请求挂起:CH1和CH5的PERINTFLG(外设中断标志)位被置起,变为“挂起”状态。
  3. 高优先级抢占:当CH4的当前字传输(注意,是当前正在搬运的这一个数据字)完成后,DMA状态机不会继续CH4的下一个字传输,而是立即暂停CH4的整个突发。它会保存CH4的当前上下文(如地址指针、剩余计数等),然后转头去服务优先级最高的CH1。
  4. 高优先级服务:DMA控制器开始处理CH1的整个突发传输,直到其全部完成。
  5. 上下文恢复:CH1完成后,控制器将之前保存的CH4上下文恢复,从中断点继续执行CH4剩余的传输。
  6. 轮询继续:CH4完成后,由于CH5仍在挂起状态,控制器接着按轮询顺序服务CH5。

这个过程的核心在于“当前字边界抢占”。DMA不会在传输一个数据字的中间强行打断,这保证了内存访问的原子性,防止出现数据错乱。这种机制对于混合了高实时性任务(电机控制ADC采样)和低实时性任务(后台数据记录)的系统至关重要,它确保了关键数据流的延迟是可预测且最小的。

2.3 过载检测逻辑:如何发现并处理丢失的DMA请求

在高速数据流场景下,另一个常见问题是DMA请求“过载”。TMS320F2838x的DMA模块内置了过载检测逻辑,这是很多开发者容易忽略但极其重要的安全特性。

其工作原理如下:

  1. 当某个通道的外设事件触发到达时,DMA会将该通道CONTROL寄存器中的PERINTFLG位置1,表示有一个传输请求“已挂号,等待处理”。
  2. 当DMA状态机开始处理该通道的突发传输时,第一件事就是清除PERINTFLG位。
  3. 关键风险窗口:在PERINTFLG被置1(事件到达)到被清除(开始处理)这个时间窗口内,如果同一个外设又产生了第二个事件触发,这个后续的触发将会被丢失。
  4. 过载标志:一旦检测到这种“上一个请求还没开始处理,下一个请求又来了”的情况,DMA会将CONTROL寄存器中的OVRFLG(过载标志)位置1。
  5. 中断通知:如果程序员事先在MODE寄存器中使能了过载中断(OVRINTE = 1),那么当OVRFLG被置位时,DMA会向CPU的PIE模块产生一个中断。这给了软件一个机会去检测和处理数据丢失事件。

这个机制告诉我们,配置DMA时,突发大小和传输速度必须匹配。如果你的ADC以1MHz的速度采样(每秒100万个数据),而你的DMA突发大小设置为传输32个字(32个采样点),那么DMA处理一次突发请求的时间内,ADC可能已经产生了多个新数据。如果这个时间超过了ADC的采样间隔,就会发生过载。解决方案通常是增大突发大小(一次搬更多数据,减少请求频率),或者优化DMA总线利用率以减少单个突发处理时间。

3. 核心寄存器配置实战指南

理解了原理,我们进入实战环节。配置DMA就像组装一台精密仪器,每一个寄存器都是一个旋钮,调错了地方,机器就可能运转失常。下面我们聚焦于与优先级和核心控制相关的几个关键寄存器。

3.1 全局控制与优先级寄存器

首先看DMA的全局寄存器组(DMA_REGS),它们管理所有通道的公共行为。

1. PRIORITYCTRL1 (偏移地址 4h) - 优先级控制寄存器这是启用通道1高优先级模式的总开关。

位域名称类型复位值描述
15-1RESERVEDR0h保留
0CH1PRIORITYR/W0hDMA通道1优先级选择。这是关键位!
0: CH1与其他通道优先级相同(默认轮询)。
1: CH1拥有高于其他所有通道的优先级。

配置关键点���

  • 配置时机:手册明确强调,只能在所有通道都禁用(RUNSTS=0)时才能修改此位。在代码中,安全的做法是在DMA初始化阶段,在启动任何通道之前,配置此寄存器。
  • 重置状态机:修改优先级后,在重新使能通道前,强烈建议向DMACTRL寄存器的PRIORITYRESET位写1,以复位轮询状态机。这能确保优先级变更立即生效,且状态机从一个干净的初始状态开始。

2. DMACTRL (偏移地址 0h) - DMA控制寄存器

位域名称类型复位值描述
1PRIORITYRESETR-0/W1S0h优先级复位。写1复位轮询状态机,服务将从第一个使能的通道重新开始。写0无效。
0HARDRESETR-0/W1S0h硬复位。写1复位整个DMA模块(类似设备复位),会中止任何当前访问。

3. PRIORITYSTAT (偏移地址 6h) - 优先级状态寄存器这是一个只读寄存器,用于调试和监控。

位域名称类型复位值描述
6-4ACTIVESTS_SHADOWR0h活跃通道状态影子寄存器仅在CH1高优先级模式下有意义。当CH1抢占服务时,被中断通道的编号会保存在这里。CH1服务完成后,该值会复制回ACTIVESTS
2-0ACTIVESTSR0h活跃通道状态。指示当前正在执行传输的通道(0表示无通道活跃)。

ACTIVESTS_SHADOW是一个非常有用的调试工具。当你的系统行为异常,怀疑是高优先级通道频繁抢占导致低优先级任务停滞时,可以通过在中断服务程序中读取这个寄存器,来记录和分析被CH1中断的是哪个通道,从而评估抢占的频繁程度和对其他任务的影响。

3.2 通道级核心寄存器配置

每个DMA通道都有一套独立的寄存器组(DMA_CHx_REGS)。以下是配置一个通道时必须关注的几个核心寄存器。

1. MODE (偏移地址 0h) - 模式寄存器此寄存器定义了通道的基本工作模式。

名称类型复位值描述与配置建议
15CHINTER/W0h通道中断使能。1=使能本通道传输完成(或开始)时向CPU产生中断。
14DATASIZER/W0h数据大小。0=16位传输;1=32位传输。注意:所有长度/步进寄存器单位仍是16位字。若选32位,地址步进需配置为2。
11CONTINUOUSR/W0h连续模式。1=传输完成后自动重装,等待下次触发;0=传输完成即停止。ADC循环缓存通常需置1。
10ONESHOTR/W0h单次触发模式与CH1高优先级互斥!1=一次事件触发完成整个传输;0=一次事件触发只完成一个突发。
9CHINTMODER/W0h中断产生模式。0=在传输开始时产生中断;1=在传输结束时产生中断。根据需求选择,通常选结束中断。
8PERINTER/W0h外设事件触发使能。必须置1,否则DMA不会响应外设或软件触发。
7OVRINTER/W0h过载中断使能。建议在高速数据流场景下使能(置1),以便在数据可能丢失时获得通知。
4-0PERINTSELR/W0h外设事件源选择(传统位)重要:此位域是遗留的,实际源选择应通过DMACHSRCSELn寄存器(属于DMA_CLA_SRC_SEL_REGS组)配置。此处通常只需设置为通道号。

2. CONTROL (偏移地址 1h) - 控制寄存器此寄存器包含状态标志和控制位。

名称类型复位值描述与操作要点
14OVRFLGR0h过载标志。只读,指示发生了事件过载。通过写ERRCLR位清除。
13RUNSTSR0h运行状态标志。只读,1表示通道已使能并等待触发。
12BURSTSTSR0h突发状态标志。只读,1表示通道正在执行突发传输。
11TRANSFERSTSR0h传输状态标志。只读,1表示通道正处于一个传输过程中(可能正在突发,也可能在等待下一个突发)。
8PERINTFLGR0h外设事件触发标志。只读,1表示有事件触发正等待处理。在突发开始时自动清零。
7ERRCLRW1S0h错误清除。写1清除OVRFLG位。
4PERINTCLRW1S0h清除外设事件触发。写1清除PERINTFLG位,用于取消一个挂起的请求。
3PERINTFRCW1S0h强制外设事件触发软件启动DMA的关键!当PERINTE=1时,写1会手动设置PERINTFLG,从而触发一次DMA传输。常用于测试或软件发起传输。
2SOFTRESETW1S0h通道软复位。写1将通道复位到默认状态(停止,计数器清零)。注意:生效有一周期延迟,操作后需加NOP指令。
1HALTW1S0h暂停通道。写1暂停通道,保持当前状态。
0RUNW1S0h运行通道启动DMA通道的标准操作。写1使能通道,并置位RUNSTS

3. 传输尺寸与地址步进寄存器这是DMA传输的“剧本”,定义了数据从哪里来、到哪里去、以什么形式搬运。

  • BURST_SIZE:定义一次突发传输包含多少个数据字(16位)。实际大小 = 设定值 + 1。例如,设为7表示一次突发传8个字。
  • TRANSFER_SIZE:定义一次完整传输包含多少个突发。实际大小 = 设定值 + 1。例如,设为15表示一次完整传输包含16个突发。
  • SRC_BURST_STEP/DST_BURST_STEP:定义在一个突发内部,每传输一个字后,源地址和目的地址的增量。可以是正数(地址递增)或负数(地址递减),用于实现线性缓冲区访问。
  • SRC_TRANSFER_STEP/DST_TRANSFER_STEP:定义在一个突发完成后,源地址和目的地址的增量。这用于在多个突发之间跳转,例如访问一个二维数组的非连续行。

4. 地址与地址环绕寄存器这是DMA的“导航系统”。

  • 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_WRAP_SIZE/DST_WRAP_SIZE环绕大小。定义在多少次突发后,地址指针跳回到开始地址并加上WRAP_STEP。用于实现循环缓冲区。禁用环绕:将其值设置为大于TRANSFER_SIZE的值(通常设为0xFFFF)。

实操心得:配置DMA地址时,强烈建议使用shadow寄存器。你可以在DMA传输进行的同时,安全地更新shadow寄存器中的目标地址,为下一次传输做好准备。而active寄存器是只读的,用于实时监控。这种“双缓冲”机制是实现乒乓缓冲区、连续流传输的基础。

4. 从理论到实践:一个ADC高优先级DMA传输的完整例程

让我们结合一个具体的场景来整合上��知识:使用TMS320F2838x的ADC模块进行高速采样,并通过DMA将数据搬运到RAM中。我们将配置CH1为高优先级模式,专门服务ADC,同时用CH2处理一个低优先级的SPI发送任务。

4.1 系统场景与硬件连接假设

  • ADC模块:采用序列发生器SEQ1,在ePWM1的周期中断触发下进行采样,采样率为1MSPS。每个采样结果为12位,存放在ADCRESULT0寄存器。我们希望DMA每次搬运16个采样点(一个突发)。
  • SPI模块:用于将一些非实时性的状态数据发送到外部设备,数据位于一个RAM缓冲区中。
  • 目标:确保ADC数据流绝对不被SPI的DMA传输阻塞,同时SPI任务也能在ADC空闲时得到执行。

4.2 步骤一:外设与DMA通道映射配置

首先,需要通过DMACHSRCSELn寄存器(属于DMA_CLA_SRC_SEL_REGS)将外设事件映射到DMA通道。

// 假设使用DriverLib库,配置DMA通道1的触发源为ADC序列发生器1的INT1事件 // 具体寄存器地址请参考手册,这里展示逻辑 DMACHSRCSEL1->SRCSEL0 = 0xXX; // 将ADCINT1事件映射到DMA CH1的触发输入 // 配置DMA通道2的触发源为SPI-A的发送缓冲区空事件 DMACHSRCSEL2->SRCSEL0 = 0xYY; // 将SPITXINT事件映射到DMA CH2的触发输入

这一步是很多新手容易遗漏的。仅仅使能了DMA通道和外设中断是不够的,必须通过DMACHSRCSELn寄存器建立正确的硬件连接。

4.3 步骤二:全局DMA与通道优先级初始化

// 1. 首先,确保所有DMA通道被禁用(通过HALT或SOFTRESET) DmaRegs.CH1.CONTROL.bit.HALT = 1; // 暂停CH1 DmaRegs.CH2.CONTROL.bit.HALT = 1; // 暂停CH2 // ... 暂停其他可能用到的通道 DELAY_US(1); // 短暂延时确保操作生效 // 2. 配置全局优先级:启用CH1高优先级模式 // 注意:必须在所有通道禁用时修改 DmaRegs.PRIORITYCTRL1.bit.CH1PRIORITY = 1; // CH1高优先级 // 3. 复位优先级状态机,确保配置生效 DmaRegs.DMACTRL.bit.PRIORITYRESET = 1; asm(\" NOP\"); // 插入一个NOP,确保写操作完成 DmaRegs.DMACTRL.bit.PRIORITYRESET = 0;

4.4 步骤三:配置高优先级通道1(用于ADC)

// 停止并软复位通道1,确保干净的状态 DmaRegs.CH1.CONTROL.bit.SOFTRESET = 1; asm(\" NOP\"); DmaRegs.CH1.CONTROL.bit.SOFTRESET = 0; // 配置模式寄存器 MODE DmaRegs.CH1.MODE.bit.CHINTE = 1; // 使能传输完成中断 DmaRegs.CH1.MODE.bit.DATASIZE = 0; // 16位传输(ADC结果是12位,按16位对齐) DmaRegs.CH1.MODE.bit.CONTINUOUS = 1; // 连续模式,ADC数据流是连续的 DmaRegs.CH1.MODE.bit.ONESHOT = 0; // 必须为0,与高优先级模式兼容 DmaRegs.CH1.MODE.bit.CHINTMODE = 1; // 在传输结束时产生中断 DmaRegs.CH1.MODE.bit.PERINTE = 1; // 使能外设事件触发 DmaRegs.CH1.MODE.bit.OVRINTE = 1; // 使能过载中断,便于监控 DmaRegs.CH1.MODE.bit.PERINTSEL = 1; // 传统位,设为通道号1 // 配置传输参数 DmaRegs.CH1.BURST_SIZE.bit.BURSTSIZE = 15; // 突发大小 = 15+1 = 16个字(16个ADC样本) DmaRegs.CH1.TRANSFER_SIZE.bit.TRANSFERSIZE = 0; // 传输大小 = 0+1 = 1个突发(每次触发搬16个字) // 地址步进:ADC结果寄存器地址固定,所以源地址步进为0。目的地址在RAM中线性存放,步进为1。 DmaRegs.CH1.SRC_BURST_STEP.all = 0; // 源突发步进:0(ADC结果寄存器地址不变) DmaRegs.CH1.DST_BURST_STEP.all = 1; // 目的突发步进:+1(RAM地址递增) DmaRegs.CH1.SRC_TRANSFER_STEP.all = 0; // 源传输步进:0 DmaRegs.CH1.DST_TRANSFER_STEP.all = 0; // 目的传输步进:0(传输大小只有1个突发,此配置无意义) // 配置地址:将ADC结果寄存器地址(例如0x0000B00)作为源,RAM缓冲区(例如0x8000)作为目的 DmaRegs.CH1.SRC_BEG_ADDR_SHADOW = (Uint32)&AdcResult.ADCRESULT0; // 源起始地址 DmaRegs.CH1.SRC_ADDR_SHADOW = (Uint32)&AdcResult.ADCRESULT0; // 源当前地址 DmaRegs.CH1.DST_BEG_ADDR_SHADOW = (Uint32)&AdcBuffer[0]; // 目的起始地址 DmaRegs.CH1.DST_ADDR_SHADOW = (Uint32)&AdcBuffer[0]; // 目的当前地址 // 禁用地址环绕(线性缓冲区) DmaRegs.CH1.SRC_WRAP_SIZE.all = 0xFFFF; DmaRegs.CH1.DST_WRAP_SIZE.all = 0xFFFF;

4.5 步骤四:配置低优先级通道2(用于SPI)

// 停止并软复位通道2 DmaRegs.CH2.CONTROL.bit.SOFTRESET = 1; asm(\" NOP\"); DmaRegs.CH2.CONTROL.bit.SOFTRESET = 0; // 配置模式寄存器 MODE DmaRegs.CH2.MODE.bit.CHINTE = 1; // 使能中断 DmaRegs.CH2.MODE.bit.DATASIZE = 0; // 16位传输 DmaRegs.CH2.MODE.bit.CONTINUOUS = 0; // 非连续模式,发完一次数据就停止 DmaRegs.CH2.MODE.bit.ONESHOT = 1; // 单次触发模式,一次触发完成整个传输(允许) DmaRegs.CH2.MODE.bit.CHINTMODE = 1; // 传输结束中断 DmaRegs.CH2.MODE.bit.PERINTE = 1; // 使能外设触发 DmaRegs.CH2.MODE.bit.PERINTSEL = 2; // 传统位,设为通道号2 // 配置传输参数:一次传输发送128个字的数据块 DmaRegs.CH2.BURST_SIZE.bit.BURSTSIZE = 7; // 突发大小 = 7+1 = 8个字 DmaRegs.CH2.TRANSFER_SIZE.bit.TRANSFERSIZE = 15; // 传输大小 = 15+1 = 16个突发 (16*8=128字) DmaRegs.CH2.SRC_BURST_STEP.all = 1; // 源突发步进:+1(RAM地址递增) DmaRegs.CH2.DST_BURST_STEP.all = 0; // 目的突发步进:0(SPI发送数据寄存器地址固定) DmaRegs.CH2.SRC_TRANSFER_STEP.all = 0; // 源传输步进:0 DmaRegs.CH2.DST_TRANSFER_STEP.all = 0; // 目的传输步进:0 // 配置地址 DmaRegs.CH2.SRC_BEG_ADDR_SHADOW = (Uint32)&SpiTxBuffer[0]; DmaRegs.CH2.SRC_ADDR_SHADOW = (Uint32)&SpiTxBuffer[0]; DmaRegs.CH2.DST_BEG_ADDR_SHADOW = (Uint32)&SpiaRegs.SPITXBUF; DmaRegs.CH2.DST_ADDR_SHADOW = (Uint32)&SpiaRegs.SPITXBUF;

4.6 步骤五:启动DMA通道与测试

// 启动通道2(低优先级SPI任务) DmaRegs.CH2.CONTROL.bit.RUN = 1; // 启动通道1(高优先级ADC任务) DmaRegs.CH1.CONTROL.bit.RUN = 1; // 此时,ADC和SPI的外设事件将触发各自的DMA传输。 // 由于CH1是高优先级,即使CH2的传输正在进行,一旦ADC事件到来,CH2会被立即暂停,CH1得到服务。 // 可以在CH1和CH2的中断服务程序中,通过读取PRIORITYSTAT.ACTIVESTS_SHADOW来观察抢占情况。

5. 常见问题排查与调试技巧实录

即使按照手册和示例配置了DMA,在实际调试中依然会遇到各种“坑”。下面是我在多个项目中总结出的常见问题与解决方法。

5.1 DMA根本不启动或只运行一次

  • 症状:使能RUN位后,DMA毫无反应,或者只完成一次传输后就停止。
  • 排查清单
    1. 外设触发映射:这是最常见的原因。确认DMACHSRCSELn寄存器是否正确配置,将外设中断事件映射到了对应的DMA通道。使用DriverLib的DMA_setInterruptSource()函数可以避免手动查表错误。
    2. PERINTE:检查通道MODE寄存器的PERINTE(外设事件触发使能)是否设置为1。如果为0,DMA只会响应软件强制触发(PERINTFRC)。
    3. RUNSTS状态:读取CONTROL寄存器的RUNSTS位。写RUN=1后,该位应变为1。如果还是0,可能是之前HALTSOFTRESET操作后没有足够延时。务必在SOFTRESETHALT操作后插入至少一个NOP指令。
    4. 连续模式:如果希望DMA在完成一次传输后自动等待下一次触发,必须将MODE寄存器的CONTINUOUS位置1。否则,传输完成后RUNSTS会自动清零。
    5. 外设本身:确认ADC/SPI等外设已正确配置并产生了中断事件。可以用示波器或IO翻转来验证中断是否真的发生。

5.2 数据地址错乱或覆盖

  • 症状:DMA传输的数据没有放到预期的内存位置,或者后续数据覆盖了前面的数据。
  • 排查清单
    1. 步进配置错误:仔细检查SRC_BURST_STEPDST_BURST_STEPSRC_TRANSFER_STEPDST_TRANSFER_STEP记住这些寄存器的单位是“地址增量”,且是16位补码形式。如果你想每次增加一个32位字(4字节),在16位模式��,步进应设为2。
    2. 环绕功能干扰:如果你不希望使用地址环绕,务必把SRC_WRAP_SIZEDST_WRAP_SIZE设置为一个大于TRANSFER_SIZE的值,例如0xFFFF(默认值)。如果错误地设置了较小的环绕值,地址会在传输中途跳变。
    3. 影子 vs 活动地址:在传输过程中,你更新的是SHADOW寄存器还是ACTIVE寄存器?在传输开始后,只有SHADOW寄存器可以安全写入ACTIVE寄存器是只读的,反映实时地址,用于调试。
    4. 缓冲区对齐:确保源和目的地址符合内存访问对齐要求。特别是使用32位模式(DATASIZE=1)时,地址必须是4字节对齐的。

5.3 高优先级通道导致低优先级通道“饿死”

  • 症状:低优先级任务(如SPI发送)偶尔或完全无法执行。
  • 分析与解决
    1. 监控抢占:在低优先级通道的中断服务程序(或主循环)中,读取PRIORITYSTAT.ACTIVESTS_SHADOW。如果这个值频繁显示为该低优先级通道的编号,说明它频繁被CH1打断。这是设计预期,但需评估影响。
    2. 优化高优先级任务:检查CH1的配置。突发大小是否过大?一个过大的突发(例如一次搬256个ADC点)会长时间占用总线,阻塞其他通道。在满足实时性前提下,尽量减小高优先级通道的突发大小。
    3. 调整轮询顺序:虽然CH2-CH6是轮询,但只有使能的通道才会进入轮询队列。如果你有多个低优先级通道,可以尝试禁用暂时不用的通道,减少轮询周期,让关键的低优先级通道获得更频繁的服务机会。
    4. 使用超时机制:对于低优先级但有时限要求的任务,可以在软件层面添加超时监控。如果等待DMA时间过长,可以改用CPU查询或中断方式处理。

5.4 过载标志频繁触发

  • 症状OVRFLG位经常被置1,或者过载中断频繁发生。
  • 排查与解决
    1. 计算请求速率:评估外设触发DMA的速率。例如,ADC采样率1MHz,如果DMA配置为每采样一次触发一次(突发大小=1),那么DMA请求间隔是1us。你需要评估DMA控制器处理一次1字突发的最短时间是否小于1us。
    2. 增大突发大小:这是最有效的解决方法。将突发大小从1调整为8或16,让ADC积累多个样本后再触发一次DMA。这样,DMA请求的频率降低了8或16倍,大大缓解了处理压力。同时,由于DMA传输的块效率,整体吞吐量可能反而上升。
    3. 检查总线竞争:DMA与CPU、CLA等其他主设备共享内存总线。如果总线被其他高优先级主设备长期占用,DMA响应延迟会增加,可能导致过载。优化其他主设备的访问模式,或者使用内存的仲裁优先级设置。
    4. 使能过载中断:务必使能OVRINTE,并在中断服务程序中处理过载事件,至少记录下错误发生的次数,以便后期分析。

调试DMA时,CCS的寄存器查看器和内存浏览器是你的最佳伙伴。实时观察CONTROL寄存器中的状态位(RUNSTS,BURSTSTS,TRANSFERSTS,PERINTFLG,OVRFLG),以及ACTIVE地址寄存器的变化,可以让你清晰地看到DMA的状态流转和数据流向,快速定位问题所在。