1. DMA基础概念:什么是DMA、DMA的工作原理、DMA与CPU数据传输对比、DMA的优缺点分析

1.1 什么是DMA?

DMA,全称Direct Memory Access,直接存储器访问。说白了,就是让外设和内存之间能直接“对话”,不需要CPU在中间当传话筒。

我刚开始做嵌入式开发时,对DMA的理解很肤浅——不就是个数据搬运工吗?后来在做一个高速数据采集项目时,CPU被中断占用了80%的资源,系统几乎卡死。那时候我才真正意识到:DMA不是简单的搬运工,它是给CPU“减负”的关键角色。

DMA本质上是一个硬件模块,它独立于CPU运行。你可以把它想象成一个智能的DMA控制器(DMAC),它知道从哪里读数据、写到哪里去、传多少字节,而且这一切都不需要CPU插手。

核心要点:DMA允许外设直接访问系统内存,无需CPU参与数据传输过程。CPU只需要告诉DMA“做什么”,剩下的由DMA自己完成。

1.2 DMA的工作原理

DMA的工作流程其实不复杂,我拆成几个步骤来讲:

  1. CPU初始化DMA:CPU设置DMA控制器的源地址、目的地址、传输长度、传输方向等参数。
  2. CPU启动DMA:CPU写一个控制寄存器,告诉DMA“可以开始了”。
  3. DMA请求总线:DMA向总线仲裁器发出总线请求信号。
  4. 获得总线控制权:CPU让出总线控制权(或者分时共享),DMA获得总线使用权。
  5. 数据传输:DMA直接从源地址读取数据,写入目的地址。每次传输后地址递增,计数器递减。
  6. 传输完成:当计数器减到0时,DMA发送中断信号给CPU,告诉它“活干完了”。

嗯,这里要注意一个细节:DMA传输期间,CPU并不是完全“死机”的。现代CPU大多支持周期窃取模式,DMA只在需要时才占用总线,其他时间CPU照常运行。

我的经验:在STM32上做DMA时,我习惯把DMA中断优先级设得比CPU主任务低一点。这样DMA传输完成后,CPU可以把手头的事处理完再响应中断,避免频繁上下文切换。

1.3 DMA与CPU数据传输对比

咱们直接上表格,一目了然:

对比项 CPU传输 DMA传输
控制方式 CPU逐字节/逐字搬运 硬件自动搬运
CPU占用率 100%(传输期间无法做其他事) 仅初始化时占用,传输过程CPU可做其他事
传输速度 受限于CPU指令执行速度 接近内存/总线带宽上限
适用场景 小数据量、非实时、简单操作 大数据量、实时性要求高、周期性传输
实现复杂度 简单,直接写循环即可 需要配置DMA控制器,稍复杂
功耗 较高(CPU一直跑) 较低(CPU可以休眠)

为什么会这样?你想想看,CPU做数据传输时,每搬一个字节都要经历“取指令→解码→执行”的流水线。而DMA是纯硬件逻辑,一条硬件通道直接搞定,速度自然快得多。

我记得在做一个4G模块数据采集项目时,用CPU轮询方式接收数据,CPU占用率飙到70%以上,系统响应变得很慢。后来改成DMA+双缓冲,CPU占用率直接降到5%以下,系统流畅多了。

1.4 DMA的优缺点分析

优点

  • 解放CPU:这是最大的好处。CPU从繁重的数据搬运中解脱出来,可以专心做算法、处理协议、响应用户交互。
  • 传输速度快:DMA的传输速率可以接近系统总线的理论带宽。比如在ARM Cortex-M7上,DMA可以达到几百MB/s的传输速度。
  • 降低功耗:CPU可以在DMA传输期间进入低功耗模式,这对电池供电的设备特别重要。
  • 支持后台传输:DMA可以在后台持续传输数据,CPU只需要在传输完成时处理一下结果。
  • 支持复杂传输模式:比如循环模式、双缓冲模式、链表模式等,灵活性很高。

缺点

  • 增加硬件复杂度:需要额外的DMA控制器,芯片面积和成本会增加。
  • 配置较麻烦:DMA的寄存器配置比CPU直接读写复杂得多,调试起来也费劲。
  • 缓存一致性问题:在带Cache的CPU上,DMA直接访问内存,而CPU可能访问的是Cache中的数据。两者不一致时,数据就乱了。
  • 不适合小数据量:如果只传几个字节,DMA的初始化开销反而比CPU直接传更大,得不偿失。
  • 总线竞争:DMA频繁占用总线,可能会影响CPU访问内存的速度。

避坑指南:我曾经在一个项目中,DMA和CPU同时访问同一个内存区域,结果数据被覆盖了。排查了两天才发现是缓存一致性问题。从那以后,我每次用DMA都会做两件事:一是确保DMA缓冲区是Cache非缓存区域(non-cacheable),二是在DMA传输前后加内存屏障指令。

1.5 什么时候该用DMA?

我个人总结了几条经验法则:

  • 数据量超过64字节,考虑用DMA
  • 传输频率超过1kHz,建议用DMA
  • CPU负载已经超过30%,优先用DMA
  • 需要低功耗运行,必须用DMA
  • 外设支持DMA接口(如SPI、UART、I2C、ADC等),尽量用DMA

举个实际例子:我在做音频采集时,ADC每10微秒产生一个16位数据。如果用CPU中断去读,每秒10万次中断,CPU基本就废了。改用DMA后,DMA自动把数据搬到内存缓冲区,CPU每1毫秒才处理一次,效率提升了10倍以上。

一句话总结:DMA不是万能的,但在大数据量、高频率、低功耗的场景下,它是你最好的帮手。学会用好DMA,是嵌入式工程师从入门到进阶的必经之路。

下一章,我会带大家深入DMA的硬件架构,看看DMA控制器内部到底长什么样,以及如何配置DMA寄存器。到时候我会拿STM32的DMA控制器做例子,手把手教大家配置。