DMA传输基础:控制器工作原理、传输模式与描述符

DMA,全称Direct Memory Access,直接存储器访问。说白了,就是让外设和内存之间能直接“对话”,不用CPU一字节一字节地搬数据。我早年做嵌入式开发时,第一次接触DMA就被它的效率震撼了——CPU终于可以腾出手来做更重要的事。

DMA控制器工作原理

DMA控制器本质上是一个硬件状态机。它接管了总线的控制权,模拟CPU执行“读-写”操作。核心流程其实很简单:

  1. 请求阶段:外设(如UART、ADC、SPI)发出DMA请求信号
  2. 仲裁阶段:DMA控制器向总线仲裁器申请总线控制权
  3. 传输阶段:获得总线后,DMA执行源地址读、目的地址写
  4. 释放阶段:传输完成,释放总线,通知CPU

关键点:DMA传输的最小单位是“节拍”(beat),一个节拍可以传输8/16/32位数据。我在调试某款车规级芯片时,发现DMA节拍配置错误导致数据错位,排查了整整两天。

DMA控制器内部有几个核心寄存器:

寄存器 作用
源地址寄存器 存放数据来源地址,支持递增/递减/固定
目的地址寄存器 存放数据去向地址,同样支持地址模式
传输计数寄存器 记录剩余待传输的节拍数
控制寄存器 配置传输方向、模式、中断使能等

三种传输模式详解

1. 单次传输模式

每次DMA请求只传输一个节拍。传输完成后,DMA释放总线,等待下一次请求。

  • 优点:对总线占用时间短,不影响其他设备
  • 缺点:每次传输都需要重新仲裁,开销较大
  • 适用场景:低速外设,如UART接收单个字符

我的经验:单次模式适合数据量小、间隔不固定的场景。我曾经用它在低功耗蓝牙项目中处理小包数据,效果不错。

2. 突发传输模式

一次DMA请求连续传输多个节拍(突发长度可配置,如4、8、16节拍)。传输期间DMA一直占用总线。

  • 优点:传输效率高,减少仲裁次数
  • 缺点:长时间占用总线,可能影响其他设备响应
  • 适用场景:高速外设,如ADC连续采样、显示刷新

注意:突发长度不是越大越好。我在某项目中设置64节拍突发,结果导致CPU中断响应延迟飙升。后来调整为16节拍,问题解决。

3. 链式传输模式

这是最灵活的模式。DMA控制器维护一个描述符链表,每个描述符定义了一次传输任务。当前任务完成后,自动加载下一个描述符。

  • 优点:支持复杂传输序列,无需CPU干预
  • 缺点:描述符管理复杂,内存开销较大
  • 适用场景:多缓冲区循环采集、数据包处理

三种模式对比一目了然:

特性 单次模式 突发模式 链式模式
总线占用 短 长 可控
传输效率 低 高 最高
CPU干预 每次请求 每次突发 几乎为零
配置复杂度 简单 中等 复杂

DMA描述符

描述符是链式传输的核心。它本质上是一个数据结构,存放在内存中。DMA控制器通过读取描述符来获取传输参数。

一个典型的描述符包含:

struct dma_descriptor {
    uint32_t src_addr;      // 源地址
    uint32_t dst_addr;      // 目的地址
    uint32_t transfer_size; // 传输大小(字节)
    uint32_t control;       // 控制字(中断使能、地址模式等)
    uint32_t next_desc;     // 下一个描述符地址(NULL表示结束)
};

描述符链表的工作流程:

  1. CPU在内存中构建描述符链表
  2. 将链表头地址写入DMA控制器的描述符指针寄存器
  3. 启动DMA,控制器自动加载第一个描述符
  4. 传输完成后,检查next_desc字段
  5. 若非空,加载下一个描述符继续传输
  6. 若为空,触发传输完成中断

避坑指南:我曾经在描述符的next_desc字段中写错了地址,导致DMA跳转到非法内存区域,系统直接挂死。后来我养成了一个习惯——描述符链表构建完成后,先遍历一遍检查指针完整性。

描述符的内存对齐也很关键。大多数DMA控制器要求描述符按8字节或16字节对齐。如果对齐不对,控制器可能读取到错误的数据。

小技巧:使用属性修饰符强制对齐,比如GCC的__attribute__((aligned(16)))。我在多个项目中都用这个办法,从未出过对齐问题。

知识体系总览

下面这张图把DMA传输的核心知识点串起来了:

DMA传输知识体系 DMA控制器 工作原理 请求 → 仲裁 → 传输 → 释放 核心寄存器:地址/计数/控制 传输模式 单次:每请求1节拍 突发:每请求N节拍 链式:描述符链表驱动 DMA描述符 源地址 | 目的地址 | 大小 | 控制 | 下一跳 链式模式通过描述符链表实现“零CPU干预”的连续传输,是高性能系统的首选

嗯,到这里DMA传输的基础就讲完了。你想想看,从单次到突发再到链式,其实就是一条从“简单但低效”到“复杂但高效”的演进路线。我在实际项目中,90%的场景都在用链式模式——虽然配置麻烦点,但带来的性能提升是实打实的。

核心总结:DMA的本质是“硬件化的数据搬运工”。理解它的工作原理、选对传输模式、用好描述符,是系统性能调优的第一步。