DMA基础概念
大家好,我是老刘。做嵌入式这一行十几年了,今天咱们聊聊DMA。你可能在芯片手册里见过它,也可能用过,但不一定真正理解它的价值。嗯,这章我们先把地基打牢。
什么是DMA
DMA,全称Direct Memory Access,直接存储器访问。说白了,就是个硬件搬运工。它可以在没有CPU干预的情况下,在内存和内存之间、内存和外设之间搬数据。
我记得刚入行那会儿,项目里用SPI读取传感器数据,每次都要CPU一条一条地读,然后手动存到缓存。后来师傅看了一眼代码,说:“你这CPU全在当苦力,换DMA吧。” 从那之后,我才算真正认识了DMA。
- CPU主导的传输:CPU执行指令,从外设读一个字节,再写到内存,循环N次。数据量一大,CPU就被“粘”在传输上。
- DMA传输:CPU配置好源地址、目标地址、数据长度,然后告诉DMA控制器:“你跑一趟。” DMA自己走总线完成传输,传完再发个中断通知CPU。
你想想看,CPU的时间是不是就省下来了?它可以去算更复杂的算法,或者处理其他中断。这就是DMA存在的理由。
DMA的工作原理
DMA干活需要几个关键元素:DMA控制器、源地址指针、目标地址指针、传输计数器和触发源。我习惯把它看作一个专门搬砖的“小机器人”。
工作流程大致是这样的:
- 配置阶段:CPU设置DMA控制器的寄存器——源地址(比如SPI数据寄存器)、目标地址(比如内存缓冲区)、要搬多少字节、触发方式(软件触发或硬件触发)。
- 启动阶段:CPU发出使能命令,或者外设发出DMA请求(比如SPI收到一个字节,发出REQ信号)。
- 传输阶段:DMA控制器获得总线控制权,从源地址读数据,写到目标地址。每次传输后计数器减1,直到为0。
- 结束阶段:传输完成,DMA控制器通知CPU(中断或标志位)。CPU可以检查结果,或者准备下一次传输。
这里有个容易踩的坑——我曾经在一个项目里,DMA配置了循环模式,结果传完数据后没有手动清标志,导致中断一直触发,系统卡死。所以一定要做好中断处理。
DMA相对于传统CPU传输的优势
传统CPU传输,数据每搬一次,CPU就要执行取指令、解码、执行、写回等一系列动作。我算过一笔账:一个32位ARM Cortex-M4跑在168MHz,用CPU循环搬1024字节,光循环开销就要几千个时钟周期。而DMA只需要配置一次,之后硬件自动搬,几乎不占CPU时间。
我把两者的对比整理成了一张表,你看一眼就能明白差距:
| 特性 | CPU直接传输 | DMA传输 |
|---|---|---|
| CPU占用率 | 100% 用于传输(阻塞) | 仅配置阶段占用,传输期间CPU可做其他事 |
| 传输效率 | 受限于指令流水线、分支预测等开销 | 硬件直接总线操作,通常比CPU快2~5倍 |
| 实时性 | 高优先级任务可能被耽误 | DMA完成才中断,不影响CPU主任务 |
| 代码复杂度 | 简单的循环指令(但CPU被粘) | 需配置寄存器结构体,但一劳永逸 |
| 适用场景 | 小数据量、非周期性的传输 | 大数据量、周期性、高速外设(如SPI ADC) |
明白了这些,再来看看DMA在实际系统中是怎么运作的。下面这张图展示了DMA传输的核心流程,以及它与CPU的关系:
看了这张图你应该能理解:CPU只负责“下命令”,DMA自己“跑腿”。跑腿期间,CPU可以干别的活,比如处理用户界面,或者跑PID算法。这就是解放生产力的关键。
我建议新手在学习DMA时,先从最简单的存储器到存储器(Mem2Mem)传输开始,用代码验证一下。比如配置一个DMA通道把数组A复制到数组B,观察复制前后CPU的负载变化。这一步做通了,再扩展到外设(比如SPI的DMA传输)。
• DMA是一种硬件数据搬运机制,绕过CPU直接访问总线。
• 配置阶段由CPU负责,传输期间CPU可并行处理其他任务。
• 相比CPU逐字节搬运,DMA效率更高、CPU占用率更低。
• 但小数据量场景慎用DMA,配置开销可能不划算。