3、DMA传输模式:单次传输、块传输、请求传输、链式传输(Scatter-Gather)

DMA的传输模式,说白了就是「数据怎么搬」的几种套路。我刚开始接触DMA时,总觉得模式越多越复杂,后来踩过几次坑才明白——每种模式都有它最合适的场景。选对了,事半功倍;选错了,调试到怀疑人生。

今天咱们就把这四种模式掰开揉碎,一个一个讲清楚。

3.1 单次传输(Single Transfer)

这是最基础的DMA模式。每次触发,只传输一个数据单元(比如一个字节或一个字)。传完就停,等下一次触发。

特点:

  • 每次传输量固定为1个数据单元
  • 需要软件或外设反复触发
  • CPU干预频率高

我在项目中遇到过用单次传输做UART接收的场景。当时觉得简单,结果发现每收一个字节就要进一次中断,CPU负载直接拉满。嗯,后来果断换了块传输。

注意:单次传输虽然简单,但频繁触发会带来大量中断开销。除非数据量极小(比如几个字节),否则不建议用在高速场景。

3.2 块传输(Block Transfer)

块传输就是一次触发,传输一整块数据。块的大小可以配置,比如64字节、256字节、1024字节等。

工作流程:

  1. 配置源地址、目的地址、块大小
  2. 启动DMA
  3. DMA自动搬运整个块
  4. 传输完成产生中断

块传输的好处很明显——一次触发,搬完一整个数据块,中断次数大大减少。我建议在ADC采集、SPI通信这类场景优先考虑块传输。

核心要点:块传输的关键在于「块大小」的配置。太小了浪费DMA能力,太大了可能阻塞其他外设请求。一般建议根据系统负载和缓冲区大小折中选择。

3.3 请求传输(Demand Transfer)

请求传输,也叫「按需传输」。它和块传输有点像,但有一个关键区别——传输过程中,如果外设的请求信号撤回了,DMA会暂停,等请求信号再次有效时继续传输。

为什么会这样?你想想看,有些外设的数据不是连续产生的。比如一个慢速传感器,数据准备好了就发个请求,没准备好就拉倒。这时候用块传输反而会出问题——DMA可能读到无效数据。

适用场景:

  • 数据产生速率不固定的外设
  • 需要与外部握手信号的接口
  • 多外设共享DMA通道的场景

我曾经在一个多路ADC采集项目里用过请求传输。每个ADC通道的数据准备好后,通过硬件信号触发DMA搬运。这样既保证了数据完整性,又避免了轮询浪费CPU。

小技巧:请求传输模式下,建议给DMA设置一个超时机制。万一外设「罢工」了,DMA不至于一直挂在那里。

3.4 链式传输(Scatter-Gather)

链式传输,也叫「分散-聚集传输」。这是DMA的高级玩法,也是我当年花了不少时间才搞明白的模式。

简单来说,链式传输允许你定义多个传输描述符(Descriptor),每个描述符指定一段源地址、目的地址和传输长度。这些描述符通过链表串起来,DMA自动按顺序执行。

为什么需要链式传输?

想象一个场景:你要从三个不连续的物理地址读取数据,然后写入一个连续的缓冲区。用普通DMA,你得配置三次、启动三次。用链式传输,一次配置,DMA自动搞定。

描述符结构示例:

struct dma_descriptor {
    uint32_t src_addr;      // 源地址
    uint32_t dst_addr;      // 目的地址
    uint32_t length;        // 传输长度
    uint32_t next_desc;     // 下一个描述符地址
    uint32_t control;       // 控制标志
};

链式传输的工作流程:

  1. 软件构建描述符链表
  2. 将链表头地址写入DMA寄存器
  3. 启动DMA
  4. DMA自动遍历链表,逐个执行传输
  5. 所有描述符执行完毕,产生完成中断
避坑指南:我曾经在链式传输上栽过跟头——描述符链表必须放在非缓存(non-cacheable)内存区域,否则CPU和DMA看到的数据不一致,导致描述符被错误解析。这个坑我调了整整两天才找到原因。

3.5 四种模式对比

模式 触发方式 传输粒度 中断频率 适用场景
单次传输 每次触发 1个数据单元 高 少量数据、简单外设
块传输 一次触发 固定块大小 低 连续数据流、批量搬运
请求传输 按需触发 可变长度 中 速率不固定的外设
链式传输 一次配置 多段不连续 极低 复杂内存布局、多缓冲区

3.6 核心知识体系

下面这张图是我自己整理的DMA传输模式知识结构,帮你快速建立整体认知:

DMA传输模式知识体系 DMA传输模式 单次传输 块传输 请求传输 链式传输 每次1个数据单元 频繁触发 固定块大小 一次触发搬完 按需触发 支持暂停/恢复 描述符链表 分散-聚集 选择建议 • 数据量小(<10字节)→ 单次传输 • 数据连续、量大 → 块传输 • 外设速率不固定 → 请求传输 • 多段不连续内存 → 链式传输 注:实际项目中常混合使用,比如链式传输中的每个描述符可以是块传输

3.7 实际项目中的选择策略

说了这么多理论,最后聊聊实际项目中怎么选。

我的经验法则:

  • 先看数据量:小于10个字节,单次传输就够了。大于100字节,考虑块传输或链式传输。
  • 再看数据连续性:数据连续且速率稳定,块传输最省心。数据不连续或速率波动,请求传输或链式传输更合适。
  • 最后看内存布局:如果源或目的地址不连续,别犹豫,直接上链式传输。
个人建议:新手从块传输入手,踩坑最少。等对DMA机制熟悉了,再尝试链式传输。链式传输虽然强大,但调试起来也最头疼——描述符链表一旦出错,DMA可能飞到不知道哪里去。

嗯,四种模式就讲到这里。每种模式都有它的脾气,摸透了就好办了。下次遇到DMA传输问题,先想想:我的数据长什么样?外设什么脾气?选对了模式,事半功倍。


可可精品资料库