3. DMA基础概念:DMA控制器工作原理、DMA传输模式、DMA与CPU协同

好,咱们进入DMA的世界。说实话,我刚开始做Camera驱动时,对DMA的理解也就停留在“它能搬数据”这个层面。直到有一次,我在一个4K分辨率的摄像头项目上,CPU占用率飙到90%多,画面还卡顿——嗯,从那以后,我才真正去啃DMA的细节。

DMA,全称Direct Memory Access,直接存储器访问。说白了,就是让硬件自己搬数据,别老麻烦CPU。你想想看,CPU本来要处理图像算法、控制逻辑,结果还得一趟趟地把摄像头数据从FIFO搬到内存,这多浪费?

3.1 DMA控制器工作原理

DMA控制器,我们通常叫它DMAC。它本质上是一个专门干“数据搬运”的小处理器。我习惯把它想象成一个智能搬运工——你告诉它“从哪搬、搬到哪、搬多少”,它就能自己干完,干完了再通知你一声。

它的核心工作流程是这样的:

  1. CPU配置DMAC:设置源地址、目的地址、传输长度、传输模式等参数
  2. DMAC获取总线控制权:通过总线仲裁,DMAC拿到系统总线的使用权
  3. DMAC执行传输:从源地址读取数据,写入目的地址,每传输一次更新地址计数器
  4. 传输完成中断:当传输计数归零,DMAC向CPU发送中断信号

关键点:DMAC内部有几个核心寄存器——源地址寄存器(SAR)、目的地址寄存器(DAR)、传输计数寄存器(TCR)、控制寄存器(CR)。我在调试一个Sensor驱动时,就遇到过TCR配置错误导致只传输了一半数据的问题,画面下半部分全是黑的。

3.2 DMA传输模式

DMA的传输模式,说白了就是“怎么搬”的问题。不同的场景用不同的模式,选错了性能会差很多。我总结一下常见的几种:

传输模式 特点 适用场景 我的经验
单次传输 一次请求传输一个数据单元 低速外设、数据量小 早期项目用过,后来发现效率太低
块传输 一次请求传输整个数据块 高速外设、大数据量 Camera驱动最常用,一次传一帧
请求传输 由外设的请求信号触发传输 外设数据准备好才传输 适合SPI、UART这类不定时数据
链式传输 多个DMA描述符链接起来 复杂传输、多缓冲区 高端Camera必备,后面会细讲

这里我要特别说说链式传输。我曾经在一个项目中,需要连续采集多帧图像,如果用单次传输,每传完一帧CPU就要重新配置DMAC,中断频繁,CPU根本忙不过来。后来改用链式传输,把多个描述符串成一个链表,DMAC自动切换,CPU就轻松多了。

小技巧:链式传输的描述符通常包含下一个描述符的地址指针。配置时一定要确保指针正确,否则会“断链”。我调试时习惯先打印出描述符链表,确认每个节点的地址都指向正确。

3.3 DMA与CPU协同

DMA和CPU怎么配合?这是个好问题。说白了,就是“谁干什么活”的分工问题。

CPU负责“动脑子”:配置DMA参数、处理传输完成后的数据、响应中断。DMA负责“出力气”:搬数据、不占用CPU时间。

协同工作的典型流程:

  1. CPU初始化DMA通道:配置源地址、目的地址、传输长度
  2. CPU启动DMA传输:写控制寄存器,使能DMA
  3. DMA独立传输:CPU可以去做其他事情,比如处理上一帧的图像数据
  4. DMA传输完成:触发中断,CPU响应中断,处理新数据

避坑指南:我曾经遇到过一个问题——DMA传输完成后,CPU去读数据,结果读到的是旧数据。为什么?因为Cache!DMA直接访问内存,但CPU可能从Cache里读数据。解决方案是:在DMA传输完成后,对相关内存区域执行Cache刷新或无效化操作。这个坑我踩过,后来每次写DMA驱动都会加上Cache操作。

还有一个容易忽略的点:总线仲裁。DMA和CPU共享系统总线,如果DMA一直占用总线,CPU就会被“饿死”。反过来,如果CPU优先级太高,DMA传输就会断断续续。我建议在Camera驱动中,给DMA设置一个适中的优先级,既能保证传输连续性,又不影响CPU的正常工作。

嗯,这里再补充一点。DMA传输时,CPU其实可以访问Cache,但不能访问DMA正在操作的内存区域。所以,我习惯把DMA缓冲区放在非Cacheable的内存区域,或者使用DMA一致性映射(dma_alloc_coherent)。这样就不用担心Cache一致性问题了。

核心总结:DMA和CPU的协同,本质上是“分工合作、互不干扰”。CPU负责控制逻辑和数据处理,DMA负责数据搬运。两者通过中断和共享内存来通信。记住一点——DMA是给CPU减负的,不是给CPU添乱的。配置不当反而会引入新的问题。

最后说一句,DMA的调试其实挺麻烦的。我建议你在开发初期就加上DMA传输的日志打印,包括每次传输的地址、长度、状态。这样出了问题能快速定位。我曾经花了两天时间查一个DMA问题,最后发现是地址对齐问题——DMA要求源地址和目的地址按4字节对齐,而我传了一个奇数地址。嗯,这种低级错误,犯过一次就不会再犯了。