3. DMA传输原理入门

在摄像头驱动中,Sensor 会以固定帧率持续输出像素数据,例如 1080p@30fps 时每秒产生约 1.5Gbit 的原始数据。如果这些数据全部由 CPU 逐字节搬运,CPU 将长期陷入“搬砖”状态,无法及时处理帧同步、曝光控制、应用调度等关键任务。DMA(Direct Memory Access,直接存储器访问)正是为解决这一矛盾而设计的硬件机制:它让外设与内存之间建立一条“旁路通道”,由独立的 DMA 控制器完成数据搬运,CPU 只负责配置和收尾。

一、DMA的基本概念与角色

DMA 控制器本质上是一个可编程的数据搬运引擎。它内部维护源地址、目的地址、传输长度、传输宽度、地址递增方式等寄存器。当外设(如摄像头接口 CSI/MIPI)产生数据请求时,DMA 控制器向总线仲裁器申请总线使用权,获得授权后直接在内存与外设 FIFO 之间读写数据,整个过程不需要 CPU 参与逐字节操作。

在摄像头驱动中,DMA 承担三个关键角色:第一,把 CSI 接收到的像素数据写入内核预分配的帧缓冲区;第二,在写入完成后触发中断,通知驱动“一帧已就绪”;第三,通过循环或链式模式维持连续多帧的自动搬运,避免每帧都重新配置。可以说,DMA 是摄像头数据通路的“传送带”,CPU 则是“调度员”。

摄像头驱动中 DMA 数据通路与 CPU 协作关系 图像传感器 Sensor / CSI CSI 接收接口 FIFO / 行缓冲 DMA 控制器 源/目的地址 传输长度 · 宽度 循环/链式描述符 帧缓冲区 DDR / SRAM CPU / 驱动 配置 · 中断处理 · 调度 像素流 DMA请求 直接写入 配置寄存器 帧完成中断 读取/处理帧数据 图例说明 数据/控制流 中断通知 CPU 访问内存 DMA 在 CSI 与帧缓冲区之间建立直接通路,CPU 仅参与配置与中断处理,不逐字节搬运像素。

二、三种典型传输模式

DMA 控制器通常支持多种传输模式,摄像头驱动中最常用的是单次、循环和链式三种。

  • 单次传输(Single/Block):配置一次,搬运指定长度后停止并产生中断。适合抓拍单帧、调试阶段或分辨率切换时的临时传输。缺点是每帧都需要 CPU 重新配置,帧率高时中断开销大。
  • 循环传输(Circular/Cyclic):DMA 在预设的缓冲区环上自动循环,搬完最后一块后自动回到起始地址继续。适合连续视频流,CPU 只需在缓冲区半满/全满中断中处理数据,显著降低配置频率。
  • 链式传输(Scatter-Gather/Linked List):DMA 按描述符链表依次搬运,每个描述符包含源地址、目的地址、长度和下一个描述符指针。适合多平面格式(如 YUV 的 Y/UV 分离存储)、多缓冲区轮转或非连续内存场景。
模式 触发方式 CPU 参与度 典型摄像头场景
单次传输 配置一次搬运一次 每帧需重新配置 单帧抓拍、调试、模式切换
循环传输 缓冲区环自动回绕 仅中断处理 连续视频流、预览
链式传输 描述符链表驱动 初始化链表后极少干预 多平面 YUV、多缓冲轮转

三、DMA 与 CPU 中断方式的对比

理解 DMA 的价值,最直接的方式是与“CPU 中断搬运”对比。在无 DMA 的系统中,CPU 需要响应每个数据就绪中断,在中断服务程序中读取外设寄存器并写入内存。以 1080p 一帧约 200 万像素、每像素 2 字节计算,若每次搬运 4 字节,一帧需要约 100 万次中断或轮询操作,CPU 几乎无法完成其他工作。

引入 DMA 后,CPU 只在“一帧搬完”或“半帧搬完”时收到一次中断,中断频率从百万级降到几十到几百赫兹。CPU 在中断中只需做三件事:确认 DMA 状态、把缓冲区标记为就绪、唤醒等待队列。数据搬运本身由 DMA 硬件并行完成,CPU 与 DMA 形成流水线协作。

核心结论:DMA 不是“更快的中断”,而是“把搬运工作从 CPU 卸载到专用硬件”。中断方式下 CPU 是搬运工,DMA 方式下 CPU 是调度员。

四、摄像头驱动中的实际配置片段

以 Linux 摄像头驱动为例,DMA 描述符的初始化通常包含源地址、目的地址、长度和回调信息。下面是一段简化的描述符配置示意:

struct dma_desc {
    u32 src_addr;      // CSI FIFO 地址
    u32 dst_addr;      // 帧缓冲区物理地址
    u32 length;        // 一行或一帧的字节数
    u32 next;          // 下一个描述符地址
    u32 ctrl;          // 中断使能、循环标志
};

/* 配置循环模式:搬完一帧后自动回到起始地址 */
desc->src_addr = CSI_FIFO_BASE;
desc->dst_addr = frame_buf_phys;
desc->length   = frame_size;
desc->ctrl     = DMA_CTRL_IRQ_EN | DMA_CTRL_CIRCULAR;

在链式模式下,驱动会预先分配多个描述符,每个描述符指向不同的帧缓冲区,形成“描述符环”。DMA 每完成一个描述符就自动加载下一个,直到环回。这样即使应用层读取速度有波动,DMA 也能持续搬运,不会因为单个缓冲区被占用而阻塞。

五、常见误区与复盘

误区一:认为 DMA 不需要 CPU 参与。DMA 的配置、描述符维护、中断处理和缓冲区管理仍然依赖 CPU。DMA 只是把“逐字节搬运”卸载,而不是把整个数据通路交给硬件。

误区二:忽略缓存一致性。DMA 直接访问内存,可能绕过 CPU Cache。如果驱动没有正确执行 cache flush/invalidate,CPU 读到的可能是旧数据。摄像头驱动中必须在 DMA 完成中断后对缓冲区做 invalidate 操作。

误区三:循环模式缓冲区太小。循环缓冲区过小会导致 DMA 回绕过快,CPU 来不及处理就发生覆盖,表现为画面撕裂或花屏。通常至少需要 3~4 帧的缓冲深度。

调试建议:先用单次模式验证 CSI 配置和帧长度是否正确,再切换到循环模式观察连续帧的稳定性;最后根据格式需求决定是否使用链式模式。每次切换模式后,检查 DMA 中断计数与帧计数是否一致,可快速定位描述符配置错误。

总结来看,DMA 在摄像头驱动中扮演的是“数据搬运加速器”的角色。单次模式适合点状采集,循环模式适合连续流,链式模式适合复杂内存布局。理解三者的差异与适用边界,是后续设计帧缓冲管理和零拷贝通路的基础。