DMA传输基础:控制器工作原理、传输模式与描述符
DMA,全称Direct Memory Access,直接存储器访问。说白了,就是让外设和内存之间能直接“对话”,不用CPU一字节一字节地搬数据。我早年做嵌入式开发时,第一次接触DMA就被它的效率震撼了——CPU终于可以腾出手来做更重要的事。
DMA控制器工作原理
DMA控制器本质上是一个硬件状态机。它接管了总线的控制权,模拟CPU执行“读-写”操作。核心流程其实很简单:
- 请求阶段:外设(如UART、ADC、SPI)发出DMA请求信号
- 仲裁阶段:DMA控制器向总线仲裁器申请总线控制权
- 传输阶段:获得总线后,DMA执行源地址读、目的地址写
- 释放阶段:传输完成,释放总线,通知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表示结束)
};
描述符链表的工作流程:
- CPU在内存中构建描述符链表
- 将链表头地址写入DMA控制器的描述符指针寄存器
- 启动DMA,控制器自动加载第一个描述符
- 传输完成后,检查next_desc字段
- 若非空,加载下一个描述符继续传输
- 若为空,触发传输完成中断
避坑指南:我曾经在描述符的next_desc字段中写错了地址,导致DMA跳转到非法内存区域,系统直接挂死。后来我养成了一个习惯——描述符链表构建完成后,先遍历一遍检查指针完整性。
描述符的内存对齐也很关键。大多数DMA控制器要求描述符按8字节或16字节对齐。如果对齐不对,控制器可能读取到错误的数据。
小技巧:使用属性修饰符强制对齐,比如GCC的__attribute__((aligned(16)))。我在多个项目中都用这个办法,从未出过对齐问题。
知识体系总览
下面这张图把DMA传输的核心知识点串起来了:
嗯,到这里DMA传输的基础就讲完了。你想想看,从单次到突发再到链式,其实就是一条从“简单但低效”到“复杂但高效”的演进路线。我在实际项目中,90%的场景都在用链式模式——虽然配置麻烦点,但带来的性能提升是实打实的。
核心总结:DMA的本质是“硬件化的数据搬运工”。理解它的工作原理、选对传输模式、用好描述符,是系统性能调优的第一步。