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控制器、源地址指针、目标地址指针、传输计数器和触发源。我习惯把它看作一个专门搬砖的“小机器人”。

工作流程大致是这样的:

  1. 配置阶段:CPU设置DMA控制器的寄存器——源地址(比如SPI数据寄存器)、目标地址(比如内存缓冲区)、要搬多少字节、触发方式(软件触发或硬件触发)。
  2. 启动阶段:CPU发出使能命令,或者外设发出DMA请求(比如SPI收到一个字节,发出REQ信号)。
  3. 传输阶段:DMA控制器获得总线控制权,从源地址读数据,写到目标地址。每次传输后计数器减1,直到为0。
  4. 结束阶段:传输完成,DMA控制器通知CPU(中断或标志位)。CPU可以检查结果,或者准备下一次传输。

这里有个容易踩的坑——我曾经在一个项目里,DMA配置了循环模式,结果传完数据后没有手动清标志,导致中断一直触发,系统卡死。所以一定要做好中断处理。

提示:DMA传输分为“单次传输”和“突发传输”。单次传一个字节就释放总线;突发可以连续传4、8、16个字节再释放,效率更高,但会占用总线时间更长,影响其他总线设备。

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搬运还大。我见过有人为了传8个字节,写了30行DMA配置代码,CPU占用率没降多少,代码可读性还差了。所以“大才大用,小才小用”。

明白了这些,再来看看DMA在实际系统中是怎么运作的。下面这张图展示了DMA传输的核心流程,以及它与CPU的关系:

DMA 传输核心流程 CPU 配置 DMA 控制器 内存 外设 (SPI) DMA请求 数据流 传输完成中断 关键点 1. 配置一次性完成 2. 传输过程无CPU介入 3. 完成才通知CPU

看了这张图你应该能理解:CPU只负责“下命令”,DMA自己“跑腿”。跑腿期间,CPU可以干别的活,比如处理用户界面,或者跑PID算法。这就是解放生产力的关键。

我建议新手在学习DMA时,先从最简单的存储器到存储器(Mem2Mem)传输开始,用代码验证一下。比如配置一个DMA通道把数组A复制到数组B,观察复制前后CPU的负载变化。这一步做通了,再扩展到外设(比如SPI的DMA传输)。

核心要点回顾:
• DMA是一种硬件数据搬运机制,绕过CPU直接访问总线。
• 配置阶段由CPU负责,传输期间CPU可并行处理其他任务。
• 相比CPU逐字节搬运,DMA效率更高、CPU占用率更低。
• 但小数据量场景慎用DMA,配置开销可能不划算。