3、DMA传输模式:单次传输、块传输、请求传输、链式传输(Scatter-Gather)
DMA的传输模式,说白了就是「数据怎么搬」的几种套路。我刚开始接触DMA时,总觉得模式越多越复杂,后来踩过几次坑才明白——每种模式都有它最合适的场景。选对了,事半功倍;选错了,调试到怀疑人生。
今天咱们就把这四种模式掰开揉碎,一个一个讲清楚。
3.1 单次传输(Single Transfer)
这是最基础的DMA模式。每次触发,只传输一个数据单元(比如一个字节或一个字)。传完就停,等下一次触发。
特点:
- 每次传输量固定为1个数据单元
- 需要软件或外设反复触发
- CPU干预频率高
我在项目中遇到过用单次传输做UART接收的场景。当时觉得简单,结果发现每收一个字节就要进一次中断,CPU负载直接拉满。嗯,后来果断换了块传输。
3.2 块传输(Block Transfer)
块传输就是一次触发,传输一整块数据。块的大小可以配置,比如64字节、256字节、1024字节等。
工作流程:
- 配置源地址、目的地址、块大小
- 启动DMA
- DMA自动搬运整个块
- 传输完成产生中断
块传输的好处很明显——一次触发,搬完一整个数据块,中断次数大大减少。我建议在ADC采集、SPI通信这类场景优先考虑块传输。
3.3 请求传输(Demand Transfer)
请求传输,也叫「按需传输」。它和块传输有点像,但有一个关键区别——传输过程中,如果外设的请求信号撤回了,DMA会暂停,等请求信号再次有效时继续传输。
为什么会这样?你想想看,有些外设的数据不是连续产生的。比如一个慢速传感器,数据准备好了就发个请求,没准备好就拉倒。这时候用块传输反而会出问题——DMA可能读到无效数据。
适用场景:
- 数据产生速率不固定的外设
- 需要与外部握手信号的接口
- 多外设共享DMA通道的场景
我曾经在一个多路ADC采集项目里用过请求传输。每个ADC通道的数据准备好后,通过硬件信号触发DMA搬运。这样既保证了数据完整性,又避免了轮询浪费CPU。
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; // 控制标志
};
链式传输的工作流程:
- 软件构建描述符链表
- 将链表头地址写入DMA寄存器
- 启动DMA
- DMA自动遍历链表,逐个执行传输
- 所有描述符执行完毕,产生完成中断
3.5 四种模式对比
| 模式 | 触发方式 | 传输粒度 | 中断频率 | 适用场景 |
|---|---|---|---|---|
| 单次传输 | 每次触发 | 1个数据单元 | 高 | 少量数据、简单外设 |
| 块传输 | 一次触发 | 固定块大小 | 低 | 连续数据流、批量搬运 |
| 请求传输 | 按需触发 | 可变长度 | 中 | 速率不固定的外设 |
| 链式传输 | 一次配置 | 多段不连续 | 极低 | 复杂内存布局、多缓冲区 |
3.6 核心知识体系
下面这张图是我自己整理的DMA传输模式知识结构,帮你快速建立整体认知:
3.7 实际项目中的选择策略
说了这么多理论,最后聊聊实际项目中怎么选。
我的经验法则:
- 先看数据量:小于10个字节,单次传输就够了。大于100字节,考虑块传输或链式传输。
- 再看数据连续性:数据连续且速率稳定,块传输最省心。数据不连续或速率波动,请求传输或链式传输更合适。
- 最后看内存布局:如果源或目的地址不连续,别犹豫,直接上链式传输。
嗯,四种模式就讲到这里。每种模式都有它的脾气,摸透了就好办了。下次遇到DMA传输问题,先想想:我的数据长什么样?外设什么脾气?选对了模式,事半功倍。
可可精品资料库