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。它本质上是一个专门干“数据搬运”的小处理器。我习惯把它想象成一个智能搬运工——你告诉它“从哪搬、搬到哪、搬多少”,它就能自己干完,干完了再通知你一声。
它的核心工作流程是这样的:
- CPU配置DMAC:设置源地址、目的地址、传输长度、传输模式等参数
- DMAC获取总线控制权:通过总线仲裁,DMAC拿到系统总线的使用权
- DMAC执行传输:从源地址读取数据,写入目的地址,每传输一次更新地址计数器
- 传输完成中断:当传输计数归零,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时间。
协同工作的典型流程:
- CPU初始化DMA通道:配置源地址、目的地址、传输长度
- CPU启动DMA传输:写控制寄存器,使能DMA
- DMA独立传输:CPU可以去做其他事情,比如处理上一帧的图像数据
- 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字节对齐,而我传了一个奇数地址。嗯,这种低级错误,犯过一次就不会再犯了。