1. DMA 基础概念:它凭什么解放 CPU?

各位同学,咱们今天聊 DMA。很多初学者觉得 DMA 是个玄学——不就是搬数据吗?CPU 自己搬不就行了?

嗯,我刚开始做嵌入式时也这么想。直到有一次做高速数据采集,CPU 被中断淹没了,系统直接卡死。从那以后,我才真正理解了 DMA 的价值。

1.1 DMA 工作原理:一个“硬件搬运工”

DMA,全称 Direct Memory Access,直接存储器访问。说白了,它是一个专门负责数据搬运的硬件模块。

CPU 就像公司的 CEO,负责决策和运算。DMA 就像快递小哥,只管把数据从 A 点搬到 B 点。CEO 不需要亲自搬货,对吧?

DMA 的工作流程其实很简单:

  1. CPU 配置 DMA:告诉它“从哪搬、搬到哪、搬多少”
  2. DMA 发起传输:直接控制总线,开始搬数据
  3. 传输完成触发中断:DMA 通知 CPU “活干完了”
  4. CPU 处理结果:CPU 只需要在最后收尾

核心要点:DMA 传输过程中,CPU 可以干别的事。这才是真正的并行工作。

1.2 DMA vs CPU:谁更适合搬数据?

我经常被问到:“DMA 和 CPU 搬数据,到底差在哪?” 咱们直接看对比:

对比项 CPU 搬运 DMA 搬运
执行方式 逐字节/逐字搬运,占用内核 硬件自动搬运,不占内核
CPU 占用率 100%(搬运期间啥也干不了) 仅配置和中断时占用(通常 <1%)
传输速度 受限于 CPU 主频和指令周期 接近总线极限速度
适用场景 小数据量(几十字节以内) 大数据量(几百字节以上)
代码复杂度 简单,直接写循环 需要配置寄存器,稍复杂
功耗 高(CPU 全速运行) 低(CPU 可以休眠)

举个例子。你想想看,如果 SPI 屏幕刷新一帧 320×240 的图像,用 CPU 逐像素搬运,CPU 得忙活好几毫秒。这期间中断来了怎么办?任务调度怎么办?

用 DMA 的话,CPU 只需要配置好源地址、目的地址和长度,然后就可以去处理其他任务,或者干脆进入休眠模式省电。DMA 搬完数据后,通过中断告诉 CPU:“搞定了,你来收尾吧。”

我的经验:在低功耗项目中,我习惯把所有的数据搬运都交给 DMA。这样 CPU 可以在大部分时间处于睡眠状态,系统功耗能降低 30%-50%。

1.3 DMA 传输三大要素:源地址、目的地址、传输长度

DMA 传输的核心,其实就是三个参数。记住这三个,你就掌握了 DMA 的精髓。

1.3.1 源地址(Source Address)

数据从哪来。可以是:

  • 外设数据寄存器:比如 UART 的 DR 寄存器、ADC 的数据寄存器
  • 内存缓冲区:比如一个数组或者 malloc 出来的内存块
  • 另一个外设:某些芯片支持外设到外设的 DMA 传输

1.3.2 目的地址(Destination Address)

数据搬到哪。常见的有:

  • 内存缓冲区:接收数据时,从外设搬到内存
  • 外设数据寄存器:发送数据时,从内存搬到外设
  • 另一个外设:比如从 SPI 直接搬到 DAC

1.3.3 传输长度(Transfer Length)

搬多少数据。单位可以是字节、半字(16位)或字(32位)。

避坑指南:我曾经在一个项目中,DMA 传输长度配置错了单位。我以为是字节数,结果配置的是字数。导致数据只搬了四分之一,系统跑飞了。排查了整整两天才找到问题。所以,一定要仔细看芯片手册,确认传输长度的单位。

1.4 DMA 传输模式:不止一种玩法

DMA 不是只能“搬一次就完事”。根据需求,有几种常见模式:

模式 说明 典型应用
单次传输 搬完指定长度就停止 一次性数据读取
循环传输 搬完后自动从头开始 音频播放、ADC 连续采样
触发传输 每次由外设触发搬一个单位 UART 逐字节收发
突发传输 一次触发搬多个单位 高速数据传输

我个人最常用的是循环传输模式。比如做音频播放时,DMA 循环从内存缓冲区搬数据到 DAC,CPU 只需要在缓冲区快空时补充数据就行。CPU 大部分时间都在睡大觉,功耗自然就低了。

1.5 DMA 传输流程:一张图看懂

下面这张图展示了 DMA 的完整工作流程。我建议你仔细看看,理解每个环节的交互关系。

DMA 传输完整工作流程 CPU DMA 控制器 内存 外设 ① 配置源/目的/长度 ② 读数据 ③ 写数据 ④ 传输完成中断 关键点 • 步骤②和③由 DMA 硬件完成 • CPU 在步骤②③期间可休眠 • 步骤④才唤醒 CPU

从这张图你能看到,DMA 传输的核心流程就是:CPU 配置 → DMA 搬运 → DMA 通知。CPU 只在第一步和最后一步参与,中间的数据搬运完全由 DMA 独立完成。

1.6 什么时候该用 DMA?

不是所有场景都适合用 DMA。我总结了几条经验:

  • 数据量大:超过 100 字节,建议用 DMA
  • 数据频繁:比如 ADC 以 1kHz 采样,每秒 1000 次中断,CPU 受不了
  • 需要低功耗:CPU 想睡觉,让 DMA 干活
  • 实时性要求高:DMA 传输不依赖 CPU 响应中断,延迟更稳定

小技巧:我习惯在项目初期就把所有外设的数据传输都设计成 DMA 方式。即使暂时用不到,预留 DMA 通道也比后期改设计容易得多。

1.7 本章小结

DMA 的本质,就是一个硬件数据搬运工。它解放了 CPU,让 CPU 能专注于更重要的计算和决策。三大要素——源地址、目的地址、传输长度——是配置 DMA 的核心。理解这些,你就迈出了 DMA 学习的第一步。