2、DMA工作原理:DMA传输的四个阶段与控制器内部结构

大家好,我是老李。今天我们来聊聊DMA的核心工作原理。说实话,DMA这东西,刚接触时觉得挺玄乎的,不就是个数据搬运工吗?但真正在项目里调通它,你会发现里面的门道还真不少。

我个人习惯把DMA传输拆成四个阶段来看:请求、响应、传输、结束。这样理解起来特别清晰。另外,DMA控制器内部长什么样,咱们也得扒开看看。

2.1 DMA传输的四个阶段

先问大家一个问题:CPU什么时候最忙?答案是——搬数据的时候。尤其是大量、高速的数据搬运,比如从外设往内存里搬,或者从内存往显示器上搬。这时候CPU要是亲自下场,那其他任务就别想干了。

DMA就是来解决这个问题的。它替CPU干脏活累活,而且干得还更快。整个传输过程,我把它分成四个阶段:

阶段一:请求阶段

外设(比如UART、ADC、SPI)准备好数据了,它会向DMA控制器发一个信号,说:“嘿,我有数据了,快来搬!”这个信号就是DMA请求。

我在项目中遇到过一个问题:外设的请求信号没处理好,导致DMA一直收不到启动信号。后来发现是外设的中断优先级和DMA请求冲突了。嗯,这里要注意,DMA请求的优先级一定要高于普通中断,否则数据可能会丢。

关键点:DMA请求可以是硬件触发(外设引脚),也可以是软件触发(写寄存器)。实际项目中,90%以上都是硬件触发。

阶段二:响应阶段

DMA控制器收到请求后,会先看看自己忙不忙。如果不忙,它就会向CPU发一个总线请求信号,说:“CPU,把总线借我用一下,我搬点数据。”

CPU这时候正在跑程序呢,但一看是DMA要总线,一般都会让出来。为什么呢?因为DMA搬数据效率高,CPU乐得清闲。CPU让出总线后,DMA就获得了总线的控制权。

这里有个细节:总线仲裁。如果多个DMA通道同时请求总线,谁先谁后?这取决于DMA控制器的仲裁策略。我见过一个坑:两个外设同时请求DMA,结果优先级低的那个一直抢不到总线,数据就丢了。后来我调整了通道优先级,问题解决。

阶段三:传输阶段

这是核心阶段。DMA拿到总线后,就开始搬数据了。搬多少?从哪搬到哪?这些信息都在DMA的配置寄存器里提前设好了。

传输模式一般有三种:

  • 单次传输:搬完一个数据单元就停,等下次请求。
  • 块传输:一次搬完一整块数据,比如256字节。
  • 突发传输:连续搬多个数据,中间不释放总线。效率最高,但占用总线时间也最长。

我个人习惯用突发传输,尤其是搬大块数据时。但要注意,突发传输可能会让CPU饿死——CPU长时间拿不到总线,其他任务就卡住了。所以突发长度要设得合理,别太贪心。

小技巧:传输阶段可以配置地址递增。比如从外设寄存器读数据,地址固定;写到内存,地址递增。这个配置一定要检查,我曾经因为地址没设递增,结果数据全写到了同一个位置,调试了半天才发现。

阶段四:结束阶段

数据搬完了,DMA会做两件事:

  1. 释放总线控制权,还给CPU。
  2. 向CPU发一个传输完成中断,告诉CPU:“活干完了,你来处理吧。”

CPU收到中断后,就可以去处理这批数据了。比如把数据存到文件里,或者显示到屏幕上。

这里有个容易忽略的点:结束阶段一定要清中断标志。我刚开始做驱动时,忘了清标志,结果中断一直触发,CPU一直在处理中断,其他事全停了。嗯,这个坑我踩过,你们别踩了。

2.2 DMA控制器内部结构

DMA控制器内部长什么样?我画了一张图,大家一看就明白。

DMA控制器内部结构 DMA主控逻辑 状态机 + 仲裁器 通道0 源地址/目的地址/长度 通道1 源地址/目的地址/长度 通道2 源地址/目的地址/长度 控制寄存器组 模式/优先级/中断使能 状态寄存器组 传输完成/错误标志 系统总线接口(AHB/APB) 外设请求 → → 中断到CPU

从这张图可以看出,DMA控制器内部主要有这几个部分:

2.2.1 多个DMA通道

每个通道对应一个外设。比如通道0给UART用,通道1给SPI用。每个通道都有自己的寄存器,用来配置源地址、目的地址、传输长度等。

我记得有一次,客户要求同时用三个外设做DMA传输。我一看,芯片只有两个DMA通道,那只能分时复用了。所以选芯片时,DMA通道数量一定要够用,否则后面很麻烦。

2.2.2 主控逻辑(状态机 + 仲裁器)

主控逻辑是DMA的大脑。它负责:

  • 接收外设的DMA请求
  • 向CPU申请总线
  • 仲裁哪个通道先传输
  • 控制传输的启动和停止

状态机一般有这几个状态:空闲、请求、传输、结束。仲裁器则决定多个通道同时请求时谁先谁后。常见的仲裁策略有固定优先级和轮询两种。

避坑指南:我曾经在一个项目里,两个通道都设成了最高优先级,结果仲裁器不知道选谁,直接罢工了。后来查手册才发现,优先级不能重复设置。所以配置时一定要仔细看芯片手册的优先级规则。

2.2.3 控制寄存器组

这里存放着DMA的工作模式配置。比如:

  • 传输模式(单次/块/突发)
  • 地址递增方式
  • 数据宽度(8位/16位/32位)
  • 中断使能

我个人习惯在初始化时,把所有控制寄存器都配好,然后一次性启动。这样不容易漏配置。

2.2.4 状态寄存器组

状态寄存器用来反映DMA当前的工作状态。比如:

  • 传输完成标志
  • 传输错误标志
  • 通道忙标志

调试时,我经常读状态寄存器来确认DMA是否正常工作。如果发现传输完成标志一直没置位,那多半是配置有问题。

2.2.5 系统总线接口

DMA通过总线接口连接到系统总线(AHB或APB)。它通过这个接口读写内存和外设寄存器。说白了,DMA搬数据就是通过这个接口完成的。

总线接口的带宽决定了DMA的传输速度。比如32位总线一次能搬4字节,64位总线一次能搬8字节。选芯片时,总线宽度直接影响DMA性能。

2.3 小结

好了,今天的内容就到这里。DMA传输的四个阶段——请求、响应、传输、结束,每个阶段都有它的关键点。DMA控制器的内部结构,说白了就是通道、主控逻辑、寄存器组和总线接口的组合。

理解这些,你就能写出靠谱的DMA驱动了。下次我们聊聊DMA的配置流程和代码实现,到时候我会拿一个实际项目中的例子来演示。

一句话总结:DMA就是CPU的“数据搬运工”,四个阶段走完,数据就搬好了。控制器内部结构不复杂,但配置细节很多,踩坑是难免的,多调试几次就熟了。

可可精品资料库