欢迎光临
我们一直在努力

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 搬运,一个样本通常要经历下面的过程:

  • 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 为例,完整链路如下:

    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.
    赞(0)
    未经允许不得转载:171主机测评 » F28379D DMA从零理解(上):DMA是什么、为什么需要它、架构与触发原理详解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址