4. DMA 传输模式:单次传输、循环传输、突发传输与链表传输
DMA 控制器支持多种传输模式,以适应不同外设与系统场景。选择正确的传输模式,直接影响数据吞吐量、CPU 干预频率以及缓冲区管理的复杂度。本章详细解析四种核心传输模式:单次传输、循环传输、突发传输与链表传输。
4.1 单次传输模式 (Single Transfer Mode)
单次传输模式是最基础的 DMA 工作方式。每次 DMA 请求只传输一个数据单元(字节、半字或字),传输完成后 DMA 通道自动停止,并产生传输完成中断(如果使能)。
- 触发方式:由外设硬件请求(如 UART RXNE、ADC EOC)逐次触发。
- 传输量:每次请求传输 1 个数据单元。
- 适用场景:低速外设、数据量小、需要精确控制每次传输的场景。
- 优点:实现简单,CPU 可在每次传输后检查数据完整性。
- 缺点:频繁的 DMA 请求与中断,总线占用率高,不适合大数据块传输。
典型应用:单次模式常用于 UART 接收不定长数据,每收到一个字节触发一次 DMA,CPU 在中断中判断帧结束条件。
4.2 循环传输模式 (Circular Transfer Mode)
循环传输模式允许 DMA 在到达传输计数终点后,自动将地址计数器重置为起始地址,继续新一轮传输。该模式无需 CPU 重新配置,即可实现连续的数据流搬运。
- 自动重载:传输计数归零后,源地址、目的地址和传输计数自动恢复初始值。
- 适用场景:音频流、ADC 连续采样、双缓冲区乒乓切换。
- 优点:零 CPU 干预,适合实时数据流。
- 注意事项:必须确保缓冲区大小固定,且 CPU 在后台及时处理已填满的半区数据。
设计提示:循环模式常与双缓冲区(Double Buffer)配合使用。DMA 在写缓冲区 A 时,CPU 处理缓冲区 B 的数据;当 DMA 切换至缓冲区 B 时,CPU 处理缓冲区 A。这种乒乓机制可有效避免数据竞争。
4.3 突发传输模式 (Burst Transfer Mode)
突发传输模式允许 DMA 在获得总线控制权后,连续传输多个数据单元(突发长度可配置,如 4、8、16 次传输),然后释放总线。该模式可显著降低总线仲裁开销。
| 参数 | 单次传输 | 突发传输 |
|---|---|---|
| 每次请求传输量 | 1 个数据单元 | N 个数据单元(突发长度) |
| 总线占用 | 频繁请求,短时占用 | 较少请求,长时占用 |
| 总线效率 | 低(仲裁开销大) | 高(减少仲裁次数) |
| 适用外设 | 低速、事件驱动型 | 高速、流式数据(如 SDIO、摄像头) |
注意:突发长度设置过大可能导致其他总线主控(如 CPU)长时间无法访问内存,影响系统实时性。建议根据系统总线负载与优先级策略合理配置。
4.4 链表传输模式 (Linked-List / Scatter-Gather Transfer Mode)
链表传输模式是高级 DMA 控制器(如 ARM PL080、STM32 GPDMA、Xilinx AXI DMA)支持的特性。它允许将多个传输描述符(Descriptor)组织成链表,DMA 自动依次执行每个描述符定义的传输任务,无需 CPU 逐次干预。
4.4.1 描述符结构
每个描述符通常包含以下字段:
- 源地址:数据读取的起始地址。
- 目的地址:数据写入的起始地址。
- 传输计数:本次传输的数据单元数量。
- 控制标志:中断使能、传输模式、地址增量等。
- 下一描述符指针:指向链表中下一个描述符的地址。
4.4.2 工作流程
- CPU 在内存中构建描述符链表。
- 将链表头指针写入 DMA 控制器的链表基址寄存器。
- 启动 DMA,控制器自动加载第一个描述符并执行传输。
- 当前描述符传输完成后,自动加载下一描述符,直至链表尾。
- 链表尾描述符的下一指针通常设为 NULL,传输完成后触发完成中断。
优势:链表模式特别适合非连续内存块传输(Scatter-Gather)、多缓冲区轮询、以及复杂的数据流处理(如网络协议栈、图像帧采集)。CPU 只需一次性构建链表,后续传输完全由 DMA 硬件自主完成。
4.5 四种模式对比总结
| 模式 | 自动重载 | CPU 干预 | 总线效率 | 典型场景 |
|---|---|---|---|---|
| 单次传输 | 否 | 每次传输后中断 | 低 | UART 逐字节接收 |
| 循环传输 | 是 | 几乎为零 | 中 | 音频流、ADC 连续采样 |
| 突发传输 | 可选 | 低 | 高 | SDIO、高速数据采集 |
| 链表传输 | 是(链式) | 一次性配置 | 最高 | Scatter-Gather、多缓冲区、网络包处理 |
选型建议:对于 RAW 格式输出场景,若数据源为连续采样(如 ADC 或摄像头),推荐使用循环传输 + 双缓冲区;若数据源为离散、非连续内存块(如从多个传感器缓冲区拼接输出),则链表传输是最优解。