前言:
本文以 TI《TMS320F2837xD Dual-Core Real-Time Microcontrollers Technical Reference Manual》中的 DMA 章节为基础,重点解释 DMA 的作用、F28379D 内部结构、触发链路、双核外设归属以及一次数据搬运究竟是怎样发生的。
1. 为什么单片机里还需要 DMA
在很多控制系统中,CPU 不仅要运行控制算法,还要不断完成数据搬运。例如:
- 从 ADC 结果寄存器读取采样值,再写入数组;
- 从 SPI 接收寄存器取出数据,再保存到 RAM;
- 将 RAM 中的一组待发送数据逐个写入 SPI 发送寄存器;
- 在不同 RAM 区域之间复制大量数据;
- 连续采集一段波形,等待整块数据完成后再执行 FFT、滤波或故障判断。
如果完全依靠 CPU 搬运,一个样本通常要经历下面的过程:
- CPU 响应中断或轮询外设状态;
- CPU 读取外设寄存器;
- CPU 计算数组地址;
- CPU 将数据写入 RAM;
- CPU 更新下标并判断缓冲区是否填满;
- 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 通过总线完成下面两个动作:
- 从源地址读取 16 位或 32 位数据;
- 将读取的数据写入目的地址。
常见方向包括:
- 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 对相应外设的访问可能无效。因此,使用共享外设前需要同时检查:
- 外设归 CPU1 还是 CPU2;
- 对应 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() 时,这些底层路由通常由库函数统一完成,不需要分别手动写 DMACHSRCSELx 和 PERINTSEL。
8. 触发事件进入DMA后发生了什么
手册中的 Peripheral Interrupt Trigger Input Diagram 展示了触发锁存逻辑。

DMA触发锁存逻辑
触发过程如下:
- 选中的外设事件到来;
- 触发锁存器被置位;
CONTROL.PERINTFLG变为 1,表示存在待处理触发;- DMA 优先级逻辑选择该通道;
- Burst 真正开始时,
PERINTFLG自动清零; - 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_STEP、DST_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清零;- 再次使用前需要重新执行
RUN或DMA_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 可以独立运行,但当它们同时访问同一总线资源或同一类外设时,需要进行仲裁。
外设帧上的固定优先级大致为:
- CLA/DMA 写;
- CLA/DMA 读;
- CPU 写;
- 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。此时需要同时检查 CPUSEL 和 SECMSEL。
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 时需要抓住四条主线:
- 触发源决定什么时候搬运;
- 源地址和目的地址决定数据从哪里到哪里;
- Burst、Transfer、Wrap 决定搬多少以及地址怎样变化;
- 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.