1. DMA 基础概念:为什么我们需要它?

大家好,我是这次课程的主讲。做了十几年嵌入式底层,我越来越觉得 DMA 是个被低估的模块。很多人会用,但真要说清楚它到底解决了什么问题,反而支支吾吾。今天咱们就从最基础的概念开始,把 DMA 的底裤扒干净。

1.1 什么是 DMA?

DMA,全称 Direct Memory Access,直接存储器访问。说白了,就是一块硬件,专门负责在内存和外设之间搬数据,不需要 CPU 插手。

你想想看,CPU 本来是要干大事的——跑算法、做决策、处理中断。结果呢?大部分时间都在那傻等,从外设读一个字节,存到内存,再读下一个字节……这不就是大材小用吗?

DMA 就是来解决这个问题的。它相当于一个专职的“数据搬运工”,CPU 只需要告诉它:“嘿,把 A 地址的数据搬 1024 个字节到 B 地址”,然后就可以去干别的了。搬完了,DMA 会发个中断通知 CPU。

核心要点:DMA 的本质是 CPU 的代理搬运工,它让 CPU 从繁重的数据拷贝任务中解放出来。

1.2 DMA 与 CPU 的数据搬运对比

我记得刚入行时,带我的老工程师说过一句话:“CPU 搬数据,就像让爱因斯坦去搬砖——不是不能搬,是太浪费了。” 这话糙理不糙。

咱们直接看对比表格,一目了然:

对比维度 CPU 直接搬运 DMA 搬运
搬运方式 逐字节/逐字读取→写入 硬件自动批量传输
CPU 占用 100% 占用,无法做其他事 仅初始化时占用,传输中释放
传输速度 受限于 CPU 主频和指令周期 接近总线极限带宽
适用场景 小数据量、随机访问 大数据块、周期性传输
功耗 CPU 全速运行,功耗高 CPU 可进入低功耗模式
实时性 受中断和任务调度影响 硬件级响应,延迟可控

举个例子,假设你要从 UART 接收 1KB 的数据。用 CPU 轮询方式,每收到一个字节 CPU 就要进一次中断,1KB 就是 1024 次中断。每次中断保存恢复上下文、跳转、判断……光这些开销就够 CPU 喝一壶的。

用 DMA 呢?配置好源地址(UART 数据寄存器)、目的地址(内存缓冲区)、传输长度(1024),然后 DMA 控制器自己搞定一切。CPU 全程可以睡大觉,或者跑更重要的任务。

我的经验:在 STM32F4 上做过实测,同样是 1KB 数据搬运,CPU 轮询方式大约占用 80% 的 CPU 时间,而 DMA 方式只占不到 5%(主要是初始化配置那几十个周期)。差距就是这么明显。

1.3 DMA 在嵌入式系统中的角色

DMA 在嵌入式系统里到底扮演什么角色?我个人的理解是:它是系统性能的“倍增器”。

咱们来看一个典型的嵌入式系统数据流:

DMA 在嵌入式系统中的角色——数据流示意图 CPU 任务调度/算法 DMA 控制器 数据搬运引擎 内存 (RAM) 数据缓冲区 外设 (UART/SPI/ADC) 外设 (定时器/DAC) 配置/启动 写入数据 请求传输 输出数据 CPU 直接访问(低效) DMA 作为 CPU 与外设/内存之间的“数据中转站”,实现零 CPU 开销的数据传输 CPU 只需配置一次,后续传输完全由 DMA 硬件自动完成

从这张图你能看到,DMA 处在 CPU、内存和外设的“三角关系”中心。它承担了最繁重的数据搬运工作,让 CPU 可以专注于控制逻辑和算法处理。

1.4 DMA 的典型应用场景

我这些年做过的项目里,DMA 几乎无处不在。这里列几个最常见的:

  • 高速数据采集:ADC 连续采样,DMA 直接把结果搬进内存环形缓冲区,CPU 定期处理即可。我在一个电力监测项目里就是这么干的,200kHz 采样率,CPU 占用不到 10%。
  • 显示刷新:LCD 或 OLED 的帧缓冲区刷新,DMA 负责把图像数据从内存搬到显示接口。没有 DMA,刷个 320x240 的屏就能把 CPU 拖死。
  • 通信协议栈:UART、SPI、I2C 等接口的数据收发。尤其是高波特率 UART(比如 921600bps),不用 DMA 的话,中断频率高到吓人。
  • 音频处理:I2S 音频流的 DMA 传输,配合双缓冲(ping-pong buffer)实现无间隙播放。
  • 存储器操作:内存拷贝、Flash 读写、CRC 计算等,都可以交给 DMA 的专用通道。

避坑指南:我曾经在一个项目里,DMA 和 CPU 同时访问同一块内存区域,结果出现了数据不一致的问题。后来才意识到,DMA 传输时 CPU 的 cache 可能还没刷新。所以,使用 DMA 一定要考虑 cache 一致性,必要时做 cache 刷新或使用非缓存内存区域。

1.5 DMA 传输的基本流程

搞清楚了 DMA 是什么,咱们再看它具体怎么工作的。一个标准的 DMA 传输流程,大致分四步:

  1. 配置阶段:CPU 设置 DMA 控制器的源地址、目的地址、传输长度、传输模式(单次/循环/突发)等参数。
  2. 启动阶段:CPU 使能 DMA 通道,DMA 控制器进入就绪状态,等待触发信号。
  3. 传输阶段:外设发出 DMA 请求(或软件触发),DMA 控制器接管总线,按配置进行数据传输。每传输一个单元,内部计数器减一。
  4. 完成阶段:传输计数器归零,DMA 控制器产生完成中断(如果使能了中断),通知 CPU 数据已就绪。

嗯,这里要注意一点:DMA 传输期间,CPU 和 DMA 是分时共享总线的。DMA 通过“周期窃取”的方式占用总线周期,CPU 会短暂暂停。不过这个暂停时间极短,通常只有几个时钟周期,对整体性能影响微乎其微。

1.6 小结

这一章咱们把 DMA 的基础概念捋了一遍。说白了,DMA 就是让专业的人干专业的事——CPU 负责动脑子,DMA 负责出力气。搞懂了这一点,后面咱们再深入 DMA 的驱动封装和调试技巧,你就能理解为什么 HAL 层要那么设计了。