DMA 基础:CPU 的数据搬运工
大家好,我是你们的讲师。咱们搞嵌入式或驱动开发的,绕不开 DMA 这个词。说白了,它就是帮 CPU 搬数据的。
我自己刚接触嵌入式开发时,觉得 DMA 这东西挺神秘。后来被一个高速数据采集项目逼得不得不搞懂它,才发现原理其实很直白。这一章咱们就把它聊透。
1.1 什么是 DMA
DMA,全称 Direct Memory Access,直接存储器访问。别被这名字唬住,你就记住一点:它是一个不需要 CPU 干预就能在内存和外设之间搬运数据的硬件模块。
没有 DMA 时,数据搬运靠 CPU 一条指令一条指令地执行,这叫 PIO(Programmed I/O)模式。有 DMA 后,CPU 只负责说一声“开始搬”,剩下的累活全交给 DMA 控制器。
我记得有一次做一个摄像头图像采集,数据量大概每秒几十兆字节。用 PIO 模式跑,CPU 占用率直接冲到 95% 以上,系统几乎卡死。换成 DMA 后,CPU 占用率掉到个位数,这才算把项目救回来。
1.2 DMA vs PIO:谁更省 CPU?
咱们把这两种方式的关键差异掰开来看。
| 对比项 | PIO 模式 | DMA 模式 |
|---|---|---|
| CPU 参与度 | 全程参与 | 仅启动和结束时介入 |
| 数据吞吐量 | 受 CPU 主频限制 | 接近总线带宽 |
| CPU 负载 | 极高,80%~100% | 很低,通常低于 5% |
| 实现复杂度 | 简单,裸机都能写 | 需要初始化 DMA 通道 |
| 应用场景 | 少量数据、低频率 | 大数据块、高频率 |
为什么 PIO 这么费 CPU?你想想看,CPU 每搬一个字节,就要取指令、解码、执行、写回。整个过程陷入到数据搬运里,根本没空干别的。而 DMA 呢?它自己有一套地址寄存器和计数器,能边搬边数,搬完了发个中断告诉 CPU “活干完了”。
1.3 DMA 工作原理:三步走
DMA 的工作流程其实就三件事:请求→传输→完成。我习惯把它拆成下面几步来理解。
- CPU 配置 DMA 控制器: 告诉它源地址在哪、目标地址在哪、要搬多少字节、用哪个外设触发。
- 外设发起 DMA 请求: 比如串口收到一个字节,它会向 DMA 控制器发请求信号。DMA 控制器拿到总线控制权,执行一次或多次传输。
- 传输完成产生中断: DMA 计数器减到 0,它产生一个中断给 CPU。CPU 就知道数据已经到位了。
这里面有个关键点:DMA 传输期间,CPU 基本上可以睡大觉或者做其他计算。因为它俩分时复用系统总线——要么 CPU 用,要么 DMA 用。不过现在很多芯片的总线架构支持并行操作,CPU 访问 Cache 时 DMA 可以同时访问主存,效率更高了。
1.4 DMA 的触发机制
DMA 不是随便什么时候都动,它需要被触发。触发方式主要有两种。
- 硬件触发: 外设发出 DMA 请求信号。例如定时器溢出、ADC 转换完成、UART 收到数据。这种最常见,适合实时性要求高的场景。
- 软件触发: CPU 写一个寄存器位来启动 DMA。适合内存到内存的拷贝,比如把一张图片从临时缓存搬到显存。
我的做法是:外设数据流固定且连续时,用硬件触发;数据块需要按条件搬运时,用软件触发。
1.5 DMA 的传输模式
不同的 DMA 控制器支持的模式不太一样,但核心就三种。
| 模式 | 描述 | 典型场景 |
|---|---|---|
| 单次传输 | 每次 DMA 请求只搬一个数据单元 | 低速外设,如按键扫描 |
| 块传输 | 一次请求搬完整个数据块 | 高速数据采集,如音频录音 |
| 循环传输 | 传输完自动重载计数器,循环不停 | 实时数据流,如麦克风输入 |
我个人最常用循环传输。比如在做一个音频回放项目时,DMA 源源不断把音频数据往 DAC 里送,CPU 只管维护一个缓冲区队列。DMA 自己在那循环搬,CPU 一点不操心。
1.6 DMA 传输流程图
这张图把整个 DMA 传输过程串起来了。
嗯,这里要注意一个细节:DMA 中断服务程序里要尽快清除中断标志位,别在里面做太多逻辑判断。我见过有人把数据处理也放在 DMA 中断里,直接导致后续传输延迟。
1.7 本章小结
DMA 说白了就一句话:让硬件干硬件的活,CPU 干 CPU 的活。它的价值不在于功能本身,而在于解放了 CPU。你掌握了 DMA,就等于有了让系统性能翻倍的能力。
1. DMA 是硬件搬运工,CPU 只负责下达命令。
2. 大数据量场景用 DMA,小数据量用 PIO。
3. 配置时注意地址对齐和中断服务程序的简洁。