第三章 DMA控制器寄存器详解

各位同学,今天我们来聊聊DMA控制器的核心——寄存器。说实话,很多做嵌入式开发的朋友,对DMA的理解就停留在「能搬数据」这个层面。但真要你配一个DMA传输,往往就卡在寄存器配置上。我当年刚接触DMA时,就被这些寄存器搞得晕头转向。

DMA控制器的寄存器,说白了就是它的「大脑」。你告诉它从哪里搬数据、搬到哪、搬多少、怎么搬,它就会乖乖干活。今天我就把这五个核心寄存器掰开揉碎了讲清楚。

3.1 源地址寄存器(Source Address Register)

这个寄存器存的是数据源头的地址。嗯,听起来很简单对吧?但坑就在这里。

关键点一:地址增长模式

源地址寄存器支持三种模式:

  • 固定模式:地址不变,适合从外设FIFO读数据
  • 递增模式:每次传输后地址自动加1/2/4(取决于数据宽度)
  • 递减模式:地址递减,用得少但某些场景很实用

我踩过的坑:有一次做音频采集,DMA从I2S接口搬数据到内存。我配了源地址递增模式,结果每次读到的数据都是乱的。折腾了半天才发现——I2S的FIFO是只读的,地址根本不能变!应该用固定模式才对。

关键点二:地址对齐

很多DMA控制器要求源地址必须按数据宽度对齐。比如你配了32位传输,源地址必须是4字节对齐的。我曾经见过一个同事,配了个奇数地址,结果DMA直接罢工,状态寄存器报了个地址错误。

我的建议:配置前先检查地址对齐。写驱动时加个断言,省得后面查bug查到怀疑人生。

3.2 目的地址寄存器(Destination Address Register)

这个和源地址寄存器是对称的,存的是数据要写到哪里去。但有个细节要注意——目的地址的增长方向可以和源地址不同。

举个例子:你想把外设FIFO的数据搬到内存的一个环形缓冲区。源地址固定(外设FIFO),目的地址递增(内存)。这种「源固定、目的递增」的模式,在数据采集里非常常见。

场景 源地址模式 目的地址模式
外设→内存 固定 递增
内存→外设 递增 固定
内存→内存 递增 递增

注意:有些DMA控制器不支持所有组合。比如低端MCU的DMA,可能只支持「源固定+目的递增」和「源递增+目的固定」两种。配之前一定要看数据手册!

3.3 传输计数寄存器(Transfer Count Register)

这个寄存器告诉DMA要搬多少数据。但这里有个容易搞混的地方——计数单位是什么?

有的DMA以字节为单位,有的以「传输次数」为单位。比如你要搬1024字节,数据宽度配了32位(4字节),那传输次数就是256次。

我个人习惯:先搞清楚数据手册里写的是「字节数」还是「传输次数」。如果是传输次数,记得算一下:次数 = 总字节数 / 数据宽度。

避坑指南:传输计数寄存器通常是递减的。每次传输完成后自动减1,减到0就触发完成中断。我曾经遇到过一个问题——传输计数配了0,结果DMA直接不干活。后来查手册才发现,有些控制器要求计数必须≥1。

3.4 控制寄存器(Control Register)

这个寄存器是DMA的「大脑」,里面塞满了各种配置位。我挑几个最重要的说:

  • 数据宽度:8位、16位、32位。要和源/目的设备匹配
  • 传输方向:内存→外设、外设→内存、内存→内存
  • 传输模式:单次传输、块传输、链表传输
  • 中断使能:传输完成中断、错误中断
  • 优先级:高、中、低

你想想看,这么多位要配,稍不留神就错了。我有个小技巧:写驱动时先定义一个结构体,把每个位域都命名好,然后用位操作赋值。这样代码可读性强,也不容易配错。

我的经验:调试DMA时,先把控制寄存器读出来打印一下。看看配进去的值对不对。很多时候问题就出在这里——你以为配了,实际上没配进去。

3.5 状态寄存器(Status Register)

这个寄存器是DMA的「反馈窗口」。它告诉你DMA当前在干嘛、有没有出错。

常见的状态位包括:

  • 传输完成标志:DMA干完活了
  • 传输进行中:DMA正在搬数据
  • 错误标志:地址错误、总线错误等
  • FIFO状态:FIFO空/满(如果有FIFO的话)

为什么要读状态寄存器? 说白了就是确认DMA有没有按你的预期工作。我调试DMA驱动时,第一步就是轮询状态寄存器,看看传输完成后标志位有没有置起来。

注意:很多状态寄存器是「写1清零」的。也就是说,你要清除某个标志位,得往那个位写1。写0是没用的。这个坑我踩过不止一次——中断处理函数里忘了清标志,结果中断一直触发,系统直接卡死。

3.6 实战经验总结

好了,五个寄存器都讲完了。最后分享几个我这些年积累的经验:

  1. 配置顺序很重要:先配源地址和目的地址,再配传输计数,最后配控制寄存器。有些DMA在控制寄存器使能后就不允许改地址了。
  2. 别忘了清状态:启动DMA之前,先把状态寄存器的旧标志清掉。否则你可能看到的是上一次传输的结果。
  3. 超时处理:DMA传输不是100%可靠的。我习惯加个超时机制——启动DMA后开个定时器,超时了还没完成就报错。
  4. 调试利器:用逻辑分析仪抓DMA的握手信号(DREQ、DACK),看看时序对不对。这比光看寄存器靠谱多了。

说实话,DMA寄存器看着多,但核心就这五个。你把这五个寄存器吃透了,DMA驱动就掌握了七八成。剩下的就是具体芯片的差异了——多看数据手册,多动手调试,慢慢就熟了。

下一章我们聊聊中断控制器,那个又是另一番天地了。