1、DMA基础概念:什么是DMA,为什么需要DMA

大家好,我是你们的嵌入式系统讲师。今天咱们聊一个经常被忽视、却又极其重要的老朋友——DMA。你肯定会想,不就是直接存储器访问吗?有什么好讲的?但说实话,我入行头两年也把它当成一个“配置好就能用”的黑盒子。直到有一次在电机控制项目里,CPU 被中断占满导致丢步,我才意识到:不懂 DMA,你永远写不出高性能嵌入式代码。

这一章我们先回到原点。不急着玩双缓冲、乒乓操作那些花哨的东西。咱们先搞清楚 DMA 到底是什么,为什么它值得你花时间学。

什么是 DMA

DMA,全称 Direct Memory Access,直接存储器访问。说白了,就是一个硬件搬运工。它在 CPU 眼皮底下,接管了内存和外设之间的数据移动,而且不需要 CPU 逐字节参与。

你想想看,传统的数据搬运是什么样子?CPU 从外设读一个字节,存到寄存器,再写到内存,循环往复。这就像大老板亲自去仓库搬箱子,浪费人才。而 DMA 控制器就是那个专门搬箱子的工人,老板(CPU)只需要告诉它:“把这一箱数据从外设搬到地址 0x20001000,搬完叫我一声。”然后 CPU 就可以去干更重要的事了。

我习惯把 DMA 比作一个可编程的硬件状态机。它有源地址、目标地址、传输长度,以及传输结束后要触发的中断。这几个寄存器配置好了,DMA 就能独立跑完整个传输过程。

核心点:DMA 不是 CPU 的替代品,而是 CPU 的助理。它处理的是“数据搬家”这种脏活累活,让 CPU 专注在控制、计算和业务逻辑上。

为什么需要 DMA

没有 DMA 的时候,嵌入式系统是怎么处理数据流的?我列一下常见的方式:

  • 轮询(Polling):CPU 不断检查外设状态寄存器,有数据就读走。CPU 被占满,还浪费电能。
  • 中断驱动(Interrupt-driven):外设准备好后触发中断,ISR 里搬运数据。中断频繁时,上下文切换开销巨大。
  • CPU 直接搬运(PIO):用循环指令逐字节读写,上面两种都是这个模式的特例。

所有这些方式都有一个共同问题:CPU 参与了每一字节的传输。当你需要 1MB/s 的数据吞吐时,CPU 可能 80% 以上的时间都在搬数据。那系统的实时性、响应速度全都会崩掉。

举个例子。我之前做的一个音频采集项目,ADC 采样率 48kHz,每个采样 16 位,每秒就是 96KB 的数据。如果用中断,48k 次中断/秒,每次中断起码消耗几十微秒。CPU 根本来不及做音频算法。改用 DMA 后,CPU 每采集一秒才处理一次数据,负载直接降到个位数。

数据传输方式的对比
方式CPU 参与程度适用场景典型吞吐
轮询全程低速外设< 1KB/s
中断驱动每帧触发一次中低速,突发传输几十 KB/s
PIO (寄存器拷贝)逐字节简单 SPI/I2C 传输几 MB/s
DMA仅发起和完成高速、大块、连续数据百 MB/s~GB/s

看到没?C 罗再厉害也不该去当守门员。DMA 的价值就是释放 CPU,让专业的人做专业的事。

一点经验:小数据块(比如几十个字节)用 DMA 可能得不偿失,因为 DMA 的配置开销本身也不小。一般我建议超过 64 字节的连续传输再考虑 DMA。你可以在项目初期做个性能摸底,很直观。

DMA 的基本工作流程

虽然不同芯片的 DMA 寄存器千差万别,但工作流程大同小异。我把它总结为四步:

  1. 配置:设定源地址、目的地址、传输长度、传输模式(单次/循环)、数据宽度等。
  2. 启动:CPU 写使能位,或者外设发出 DMA 请求,DMA 控制器开始工作。
  3. 传输:DMA 在系统总线上逐个周期搬运数据。这个过程不占用 CPU,CPU 可以运行其他代码或进入低功耗模式。
  4. 完成:达到计数后,DMA 停止并可选地触发中断。CPU 在中断服务函数里处理这批数据。

嗯,这里要注意,DMA 传输期间会占用系统总线。如果 CPU 也需要访问总线(比如取指令),会出现总线仲裁。大多数 MCU 的 DMA 优先级高于 CPU,所以密集型 DMA 会拉长 CPU 的指令执行周期。这就是为什么做实时系统时,要留尽量避开 DMA 对关键时序的影响——我曾在某个电机控制项目里吃过这个亏,后来加了一级指令缓存才解决。

避坑指南:我曾经在 STM32F4 上配置 DMA 时,源地址和目标地址都设为 32 位对齐,但数据长度设成了奇数个字节,结果最后两个字节数据全是错的。后来查勘误手册才知道,该系列 DMA 要求传输长度必须与数据宽度对齐。配置寄存器前一定仔细看参考手册的“限制”章节。

DMA 在系统中的位置

为了让你有个直观的印象,我画了一张 DMA 数据传输示意图。CPU、DMA 控制器、内存和外设都挂在系统总线上,DMA 是独立的总线主控。

DMA 数据传输示意图 系统总线 CPU DMA 控制器 内 存 外 设 配置/启动 搬数据 DMA 请求 / 数据 说明: ① CPU 配置 DMA 控制器的源、目的、长度、模式。 ② 外设发出 DMA 请求(或 CPU 软件触发),DMA 开始自动传输。 ③ 传输过程中 CPU 可以执行其他代码,DMA 完成后通过中断通知 CPU。
一个提醒:图中显示的是常见架构——DMA 和 CPU 共享系统总线。有些高性能芯片(比如 Xilinx Zynq)拥有独立的 AXI 总线给 DMA,或者每个 DMA 通道有自己专用的总线端口,这样就不会和 CPU 抢总线。选芯片时你可以注意一下“DMA 总线矩阵”这个参数。

好了,通过这一章你应该明白了:DMA 的本质是一个硬件数据搬运引擎,它存在的理由就一个——让 CPU 从低效的数据搬运中解放出来,去处理真正需要复杂逻辑的事情。接下来我们会一步步深入 DMA 的配置细节、双缓冲与乒乓策略,以及如何在工程中安全高效地使用它。

(对了,如果你现在手头有一个正在开发的嵌入式项目,建议先翻翻芯片手册的 DMA 章节,看看有几个通道、支持什么样的触发源。心里有个底,后面学习会更顺手。)