F28379D DMA从零理解(上):DMA是什么、为什么需要它、架构与触发原理详解

前言:

本文以 TI《TMS320F2837xD Dual-Core Real-Time Microcontrollers Technical Reference Manual》中的 DMA 章节为基础,重点解释 DMA 的作用、F28379D 内部结构、触发链路、双核外设归属以及一次数据搬运究竟是怎样发生的。

1. 为什么单片机里还需要 DMA

在很多控制系统中,CPU 不仅要运行控制算法,还要不断完成数据搬运。例如:

  • 从 ADC 结果寄存器读取采样值,再写入数组;
  • 从 SPI 接收寄存器取出数据,再保存到 RAM;
  • 将 RAM 中的一组待发送数据逐个写入 SPI 发送寄存器;
  • 在不同 RAM 区域之间复制大量数据;
  • 连续采集一段波形,等待整块数据完成后再执行 FFT、滤波或故障判断。

如果完全依靠 CPU 搬运,一个样本通常要经历下面的过程:

  1. CPU 响应中断或轮询外设状态;
  2. CPU 读取外设寄存器;
  3. CPU 计算数组地址;
  4. CPU 将数据写入 RAM;
  5. CPU 更新下标并判断缓冲区是否填满;
  6. CPU 退出中断,恢复原来的程序。

单次操作看起来并不复杂,但当 ADC 采样频率、SPI 数据率或通道数量升高后,CPU 会把大量时间花在"搬数据"上,而不是花在电流环、速度环、保护逻辑和通信协议上。

DMA 的价值,就是把这类规则、重复的数据搬运交给专用硬件完成。


CPU搬运与DMA搬运对比

2. DMA到底是什么

DMA 的全称是 Direct Memory Access,即直接存储器访问。

在 F28379D 中,DMA 可以理解为一个独立于 C28x CPU 的硬件数据搬运器。CPU 只负责提前告诉 DMA:

  • 数据从哪里读取;
  • 数据写到哪里;
  • 每次搬多少;
  • 地址每次怎样变化;
  • 由哪个外设事件启动;
  • 全部完成后是否产生中断。

配置完成后,DMA 会等待触发事件。当 ADC、ePWM、SPI、CPU Timer、外部中断或软件触发到来时,DMA 自动获得总线访问权,读取源地址中的数据,再写入目的地址。CPU 不需要为每个数据执行一次读写指令。

因此,DMA 并不是一块存储器,也不是一种通信协议。它本质上是一个具有以下能力的硬件状态机:

  • 接收外设或软件触发;
  • 产生源地址和目的地址;
  • 读取数据并写入数据;
  • 自动更新地址;
  • 统计已经完成的 Word、Burst 和 Transfer;
  • 在指定时刻产生 DMA 通道中断;
  • 检测触发来不及处理造成的 Overrun。

DMA 最重要的作用不只是"复制速度快",而是让数据搬运和 CPU 运算并行进行,从而降低 CPU 实时负担。

3. F28379D DMA的主要特性

F28379D 的每个 CPU 子系统均包含一个 DMA 模块。一个 DMA 模块具有 6 个相互独立的通道,每个通道都能分别配置源地址、目的地址、触发源、数据宽度和中断。

主要特性如下:

特性 含义
6 个独立通道 多组数据搬运任务可以分别配置
每通道独立 PIE 中断 可以在传输开始或结束时通知 CPU
外设事件触发 ADC、ePWM、SPI、SDFM、CPU Timer、XINT、CLB 等均可成为触发源
软件强制触发 便于测试 RAM 到 RAM 的数据搬运
16 位或 32 位数据宽度 SPI DMA 受限于 16 位
地址自动递增、递减或保持 支持连续数组、固定外设寄存器和逆序数据
Burst、Transfer、Wrap 三层地址控制 支持块传输、二维重排、循环缓冲和双缓冲
Channel 1 高优先级 适合高速 ADC 等低延迟任务
无仲裁时约 3 个周期完成一次 Word 搬运 实际时间还会受每个 Burst 的启动开销和总线冲突影响

这里的"通道"不是一条独立物理总线,而是一套独立的配置和状态寄存器。6 个通道最终仍要共享 DMA 状态机和相关总线资源,因此需要通道优先级与仲裁机制。

4. 从F28379D总框图理解DMA的位置

TI 手册中的 DMA Block Diagram 展示了 DMA 与 ADC、SPI、ePWM、SDFM、RAM、CPU 和 PIE 之间的关系。


F28379D DMA总体框图

这张图需要分成"触发路径"和"数据路径"两部分理解。

4.1 虚线表示触发路径

图中的虚线从 ADC、ePWM、SPI、SDFM、CPU Timer、外部中断等模块进入 DMA Trigger Source Selection。它们只负责告诉 DMA:

某个事件已经发生,现在应该启动一次 DMA Burst。

触发信号本身不携带要搬运的数据。例如,ADCAINT1 可以触发 DMA,但 DMA 真正读取的数据地址仍由 SRC_ADDR 决定。

因此,DMA 的"触发源"和"数据源"是两个不同概念:

  • 触发源决定什么时候搬;
  • 源地址决定从哪里搬。

ADC 中断可以触发 DMA 去读 ADCRESULT,也可以触发 DMA 去读 RAM;ePWM SOCA 也可以触发 DMA 更新某个外设寄存器。只要总线访问路径允许,触发事件与数据地址并不要求来自同一个模块。

4.2 实线表示数据访问路径

图中的实线表示 CPU 和 DMA 访问外设寄存器或 RAM 的数据总线。DMA 通过总线完成下面两个动作:

  1. 从源地址读取 16 位或 32 位数据;
  2. 将读取的数据写入目的地址。

常见方向包括:

  • ADCRESULT → RAM;
  • SPI RXBUF → RAM;
  • RAM → SPI TXBUF;
  • RAM → ePWM CMPA;
  • GS RAM → GS RAM;
  • RAM → DACVALS。

4.3 DMA中断进入PIE

每个 DMA 通道都有独立的中断输出,可接入 PIE。中断可以配置为:

  • 在一次 Transfer 刚开始时产生;
  • 在一次 Transfer 全部结束时产生;
  • 检测到 Overrun 时产生。

通常,单缓冲采集在传输结束时中断;连续 Ping-Pong 双缓冲可以利用传输开始中断及时更新下一轮 Shadow 地址。

5. F28379D是双核器件:DMA也有CPU1和CPU2之分

F28379D 内部包含 CPU1 子系统和 CPU2 子系统,每个子系统都包含:

  • 一个 C28x CPU;
  • 一个 CLA;
  • 一个 DMA。

部分外设可以在 CPU1 与 CPU2 之间分配。手册中的 Common Peripheral Architecture 展示了 CPU、CLA、DMA 与共享外设之间的关系。


双核公共外设架构

5.1 CPUSEL决定外设归哪个CPU子系统

共享外设通常通过 CPUSELx 位确定归属:

  • 分配给 CPU1 后,由 CPU1 子系统访问;
  • 分配给 CPU2 后,由 CPU2 子系统访问。

如果外设并不属于当前 CPU 子系统,那么该子系统中的 CPU、DMA 或 CLA 对该外设的写操作可能被忽略,读操作可能返回 0。

这也是双核工程中 DMA"没有报错但数据始终不变化"的常见原因之一。

5.2 SECMSEL决定二级主设备是DMA还是CLA

一个 CPU 子系统除了 C28x CPU 这个主设备外,还可以选择 DMA 或 CLA 作为 Secondary Master。该选择由对应的 SECMSEL 配置决定。

如果外设归 CPU1,但 CPU1 子系统的 Secondary Master 选择了 CLA,那么 CPU1 DMA 对相应外设的访问可能无效。因此,使用共享外设前需要同时检查:

  1. 外设归 CPU1 还是 CPU2;
  2. 对应 CPU 子系统中,Secondary Master 选择的是 DMA 还是 CLA。

5.3 DMA能访问外设,不代表外设能直接触发DMA

图中 eCAP、eQEP、CMPSS、DAC 等模块可以位于 DMA 可访问的外设总线上,但手册特别标出了部分模块没有集成 DMA Trigger 能力。

这意味着:

  • DMA 可以读写这些模块的寄存器;
  • 但这些模块自身未必能直接产生 DMA 触发;
  • 可以使用 ePWM、CPU Timer、XINT 或软件事件作为触发源,再让 DMA 访问它们。

"能否被 DMA 访问"和"能否触发 DMA"必须分开判断。

6. DMA为什么是事件驱动,而不是自动定时运行

F28379D DMA 是事件驱动状态机。DMA 通道启动后,通常只是进入等待状态,并不会自行周期性搬运数据。

真正启动 Burst 的事件可以来自:

  • ADC 中断事件;
  • ePWM SOCA 或 SOCB;
  • SPI TX/RX DMA 事件;
  • SDFM 数据就绪事件;
  • CPU Timer 0、1、2;
  • XINT1~XINT5;
  • McBSP;
  • CLB 中断;
  • 软件强制触发。

如果希望 DMA 周期性运行,可以把 CPU Timer 或 ePWM 周期事件配置为 DMA 触发源。周期性来自定时器或 ePWM,而不是 DMA 内部自己产生周期。

7. DMA触发选择电路为什么有两级选择

手册中的 DMA Trigger Architecture 展示了触发信号进入 6 个 DMA 通道的过程。


DMA触发选择架构

这张图可以按两级多路选择器理解。

7.1 第一级:DMACHSRCSEL选择系统触发源

系统中存在大量 DMA 触发源。DMACHSRCSELx 为每个 DMA 通道选择一个具体事件,例如:

  • ADCAINT1_DMA
  • EPWM1_SOCA
  • CPU_TINT0
  • SPIA_RXDMA

每个通道可以选择不同事件,因此可以同时构建:

  • DMA CH1:ADC A 采样结果写入 RAM;
  • DMA CH2:SPI A 接收数据写入 RAM;
  • DMA CH3:RAM 数据写入 SPI A;
  • DMA CH4:定时更新 ePWM CMPA。

7.2 第二级:通道内部PERINTSEL选择输入线

每个通道的 MODE.PERINTSEL 再选择 DMA 内部的触发输入。正常配置时,CH1 对应选择输入 1,CH2 对应输入 2,依次类推。

使用 Driverlib 的 DMA_configMode() 时,这些底层路由通常由库函数统一完成,不需要分别手动写 DMACHSRCSELxPERINTSEL

8. 触发事件进入DMA后发生了什么

手册中的 Peripheral Interrupt Trigger Input Diagram 展示了触发锁存逻辑。


DMA触发锁存逻辑

触发过程如下:

  1. 选中的外设事件到来;
  2. 触发锁存器被置位;
  3. CONTROL.PERINTFLG 变为 1,表示存在待处理触发;
  4. DMA 优先级逻辑选择该通道;
  5. Burst 真正开始时,PERINTFLG 自动清零;
  6. DMA 按配置搬运一个 Burst 或整个 Transfer。

软件也可以操作同一套锁存逻辑:

  • PERINTFRC:软件强制置位触发标志;
  • PERINTCLR:取消尚未处理的触发;
  • PERINTFLG:查看是否有触发等待处理。

需要注意,软件强制触发仍要求通道的 PERINTE 已使能。关闭 Trigger 后,外设事件和软件强制触发都不能启动 Burst。

9. Word、Burst和Transfer分别是什么

DMA 初学者最容易混淆的概念就是 Word、Burst 和 Transfer。

9.1 Word:一次基本读写的数据

Word 是 DMA 一次从源地址读取并写入目的地址的数据单位。

  • 16 位模式:一次读写 16 位;
  • 32 位模式:一次读写 32 位。

但是,DMA 的 Size 和 Step 参数统一以 16 位地址单元为基准。这一点在 32 位模式下尤其容易配置错误。

9.2 Burst:一次触发直接执行的一小组数据

默认情况下,一个触发事件启动一个 Burst。

若一个 Burst 包含 8 个 16 位 Word,则一个触发会连续搬运 8 个数据。Burst 内每完成一个 Word,源地址和目的地址按照 SRC_BURST_STEPDST_BURST_STEP 更新。

9.3 Transfer:若干个Burst组成的完整任务

一个 Transfer 由多个 Burst 构成。例如:

  • 每个 Burst 搬 8 个 Word;
  • 一个 Transfer 包含 16 个 Burst;
  • 则完整 Transfer 共搬运 128 个 16 位 Word。

在寄存器层面:

Nword=(BURST_SIZE+1)(TRANSFER_SIZE+1) N_{word}=(BURST\_SIZE+1)(TRANSFER\_SIZE+1) Nword=(BURST_SIZE+1)(TRANSFER_SIZE+1)

这里的 +1 非常重要,因为底层 SIZE 寄存器保存的是"实际数量减 1"。

而 Driverlib 的 DMA_configBurst()DMA_configTransfer() 参数使用实际数量。例如需要每个 Burst 搬运 8 个 Word,应传入 8,而不是 7。

10. ONESHOT与CONTINUOUS到底控制什么

10.1 ONESHOT控制"一次触发搬多少"

ONESHOT = 0

  • 一个触发只搬一个 Burst;
  • 若一个 Transfer 有 16 个 Burst,则需要 16 次触发;
  • 每个 Burst 结束后,DMA 可以切换到其他待处理通道;
  • 更利于多通道公平共享 DMA 带宽。

ONESHOT = 1

  • 第一次触发到来后,DMA 不再等待后续触发;
  • 连续执行整个 Transfer;
  • 适合一次性 RAM 块复制;
  • 但长 Transfer 可能长期占用 DMA 带宽。

10.2 CONTINUOUS控制"一个Transfer结束后是否继续待命"

CONTINUOUS = 0

  • 一次 Transfer 完成后,通道停止;
  • RUNSTS 清零;
  • 再次使用前需要重新执行 RUNDMA_startChannel()

CONTINUOUS = 1

  • Transfer 完成后,DMA 重新加载计数和 Shadow 地址;
  • 通道保持运行状态;
  • 等待下一次触发;
  • 适合连续 ADC 采样、通信接收和循环缓冲。

ONESHOT 与 CONTINUOUS 不是同一个概念:

  • ONESHOT 决定一个触发覆盖一个 Burst 还是整个 Transfer;
  • CONTINUOUS 决定完整 Transfer 结束后通道是否继续工作。

11. 一个典型的ADC采样链路

以 ePWM 定时触发 ADC,再由 ADC 中断触发 DMA 为例,完整链路如下:

text 复制代码
ePWM 到达设定时刻
        ↓
ePWM SOCA 启动 ADC SOC
        ↓
ADC 完成采样与转换
        ↓
ADCAINT1 事件到来
        ↓
DMA CH1 的 PERINTFLG 置位
        ↓
DMA 从 ADCARESULT0 读取结果
        ↓
DMA 将结果写入 adcBuffer[]
        ↓
目的地址自动加 1
        ↓
缓冲区填满后产生 DMA 中断
        ↓
CPU 对整块数据执行控制、滤波或频谱分析

这种结构与"ADC 每采一次就进入 CPU 中断、CPU 手动保存一次数据"相比,具有三个明显优势:

  • CPU 中断次数显著减少;
  • 采样结果写入 RAM 的时间更加确定;
  • CPU 可以按整块数据进行处理,程序结构更清晰。

12. CPU与DMA会不会同时抢总线

会。

DMA、CPU 和 CLA 可以独立运行,但当它们同时访问同一总线资源或同一类外设时,需要进行仲裁。

外设帧上的固定优先级大致为:

  1. CLA/DMA 写;
  2. CLA/DMA 读;
  3. CPU 写;
  4. CPU 读。

因此,当 CPU 和 DMA 在同一周期访问同一外设帧时,DMA 可能先完成当前访问,CPU 暂停若干周期。CPU 并不是一直等到整个 Transfer 结束,只会等当前发生冲突的 DMA 访问完成。

ADC 结果寄存器是一个特殊情况。F28379D 为不同总线主设备提供了重复的 ADC Result 访问路径,因此 CPU、DMA 和 CLA 可以同时读取 ADC 结果,而不需要彼此仲裁。

还应避免 CPU 与 DMA 同时写同一个地址。特别是 CPU 正在执行读---改---写操作时,如果 DMA 在中间写入同一位置,DMA 写入的数据可能被 CPU 随后的写操作覆盖。

13. 初学DMA最容易出现的错误

13.1 只启动通道,没有使能触发

DMA_startChannel() 只会让通道进入 RUN 状态并等待事件。如果没有执行 DMA_enableTrigger(),外设事件和软件强制触发都不能启动搬运。

13.2 触发源选对了,数据源地址却写错了

触发源只决定"什么时候开始",不会自动决定"读取哪个寄存器"。ADC 中断触发 DMA 后,仍需把源地址明确配置为 ADCARESULT_BASE + ADC_O_RESULTx

13.3 双核外设归属错误

SPI、ePWM 等共享外设可能归 CPU2,但 DMA 配置在 CPU1。此时需要同时检查 CPUSELSECMSEL

13.4 32位模式仍按16位地址单位配置

即使选择 32 位传输,Size 和 Step 仍以 16 位地址单元为单位:

  • 连续移动一个 16 位地址:Step = 1;
  • 连续移动一个 32 位数据:Step = 2;
  • 搬运 3 个 32 位数据:对应 6 个 16 位地址单元。

13.5 Burst为1时误用BURST_STEP

当 Burst 只有 1 个 Word 时,Burst 内不存在"下一个 Word",因此地址通常需要依靠 TRANSFER_STEP 在每次 Burst 结束后前进。

例如 ADC 每次触发只读取一个结果并写入数组:

  • BURST_SIZE = 1 Word
  • DST_BURST_STEP = 0
  • DST_TRANSFER_STEP = 1

13.6 触发频率超过DMA处理能力

如果新的触发到来时,上一触发仍处于 Pending 状态,DMA 会设置 Overrun 标志,额外触发可能丢失。此时应检查:

  • Burst 是否过长;
  • 多通道是否争用严重;
  • 是否存在 CPU/CLA 总线冲突;
  • ADC 或 SPI 触发频率是否过高;
  • 是否需要将高速任务放到 Channel 1 高优先级。

13.7 ADC结果可能读到旧值

手册特别提示需要查看器件勘误中的"ADC:DMA Read of Stale Result"条目。设计 ADC 与 DMA 触发链路时,应确认 ADC 结果已经更新,再允许 DMA 读取结果寄存器。

14. 小结

F28379D DMA 可以概括为:

一个由外设事件驱动、能够独立产生地址并在外设与内存之间自动搬运数据的硬件状态机。

理解 DMA 时需要抓住四条主线:

  1. 触发源决定什么时候搬运;
  2. 源地址和目的地址决定数据从哪里到哪里;
  3. Burst、Transfer、Wrap 决定搬多少以及地址怎样变化;
  4. CPU 只在配置和整块数据处理阶段参与,不再逐个搬运样本。

下一篇将进一步分析 Shadow/Active 地址寄存器、Burst/Transfer/Wrap 的精确地址变化、循环缓冲与 Ping-Pong、通道优先级、流水线吞吐率、Overrun 以及 Driverlib 配置代码。

参考资料

  • Texas Instruments, TMS320F2837xD Dual-Core Real-Time Microcontrollers Technical Reference Manual, Direct Memory Access Chapter, SPRUHM8K, revised May 2024.
  • Texas Instruments, F2837xD Driverlib API Guide - DMA Module.
相关推荐
2CM_Embed2 天前
F28379D 下载后 VOFA+ 无数据、Reset 后恢复:一次 TZ 中断误触发排查记录
笔记·单片机·sci·c2000·f28379d·vofa·tz
轻微的风格艾丝凡2 个月前
两电平三相VSC整流模式从不控整流平滑切换至有源整流调试记录
算法·dsp·c2000
fffzd2 个月前
STM32:串口--DMA
stm32·单片机·嵌入式硬件·串口·dma·回调函数
bbaydnog2 个月前
嵌入式面试高频题第5弹:DMA原理、看门狗机制、低功耗模式,这3个搞不懂简历直接被筛
面试·dma·嵌入式
yutian06062 个月前
TI C2000 系列 280049 等 JTAG 配置为GPIO后的恢复方法
c2000
老李的森林2 个月前
嵌入式开发--STM32用DMA+IDLE中断方式串口接收不定长数据--之2
stm32·单片机·嵌入式硬件·串口·dma·idle中断
东成20222 个月前
ch340驱动的串口可以寻址DMA吗
串口·dma·ch340
CinzWS3 个月前
DMA控制器 - 沉默的加速器:驾驭通道仲裁、传输握手与内存一致性的双刃剑
arm开发·dma·嵌入式
NQBJT3 个月前
DMA —— 让 CPU “偷懒”的数据搬运工
stm32·单片机·dma·嵌入式