2. DMA传输模式:单次、块、链传输
DMA传输模式,说白了就是决定「一次搬运多少数据、搬完后怎么办」。我当年刚接触DMA时,觉得能传输就行,模式什么的无所谓。直到有一次项目功耗超标,才发现选错了模式,CPU被拖累得够呛。
搞懂这三种模式,你就能精准控制CPU负载。咱们一个一个看。
2.1 单次传输模式
这是最基础的DMA模式。一次请求,只传输一个数据单元(比如一个字节或一个字)。传输完就停,等着下一个请求。
我曾经在音频采集项目里用过单次传输,结果CPU每秒要响应几千次中断。你说这DMA还有什么意义?
工作流程:
- CPU配置源地址、目的地址、传输大小(1单元)
- DMA搬运1个单元
- 触发中断,通知CPU处理
- CPU重新配置下一次传输
你想想看,每次传输都要CPU介入。这跟CPU自己搬数据有什么区别?唯一的好处是,传输本身不占CPU时钟周期。但中断开销全在CPU头上。
| 特性 | 描述 |
|---|---|
| 中断频率 | 极高(每个数据单元一次) |
| 适用场景 | 极低速外设(如按键、传感器采样) |
| CPU负载 | 高(被频繁中断淹没) |
我个人习惯,只有在传输频率低于1KHz时才考虑单次模式。否则CPU光伺候中断就够呛了。
2.2 块传输模式
块传输模式,也叫突发传输。它把多个数据单元打包成一个「块」,一次请求就把整个块搬完。
举个例子:
- 配置块大小为1024字节
- DMA连续搬运1024次(每次1字节)
- 全部完成后触发一次中断
这里的关键是「连续搬运」——DMA独占总线,一口气把数据搬完。中间不释放总线控制权。
我在处理高速数据采集卡时遇到过一个问题:块传输虽然减少了中断次数,但总线被DMA占着不放,CPU在传输期间无法访问内存。如果传输块太大,CPU响应会延迟。
核心权衡:块越大,中断次数越少,但CPU内存访问延迟越高。
| 块大小 | 中断次数(1MB数据) | 总线占用持续时间 |
|---|---|---|
| 64字节 | 16384次 | 极短 |
| 1KB | 1024次 | 中等 |
| 16KB | 64次 | 较长 |
块传输适用场景:
- 连续数据流(如音频、视频)
- 外设速度匹配(如SPI、I2C)
- CPU可以忍受短时总线延迟的场景
2.3 链传输模式
链传输,这是真正的大杀器。它把多个传输任务连成一条「链」,DMA自动执行下一个,CPU全程不用管。
核心原理:每个传输任务称为一个「描述符」,描述符中除了配置信息,还包含指向下一个描述符的指针。
// 描述符结构示意
struct dma_descriptor {
uint32_t src_addr; // 源地址
uint32_t dst_addr; // 目的地址
uint32_t transfer_size; // 传输大小
uint32_t next_desc; // 指向下一个描述符
uint32_t control; // 控制字段
};
// 链式描述符示例
struct dma_descriptor desc1, desc2, desc3;
desc1.next_desc = &desc2;
desc2.next_desc = &desc3;
desc3.next_desc = NULL; // 链尾
为什么要把多个块串起来?我举个实际案例:图像采集系统中,一帧图像分布在3个不同缓冲区。如果用块传输,CPU得配置3次;用链传输,配置一次,DMA自动搞定三个缓冲区。
链传输的三大优势:
- 零CPU干预:配置完链后,CPU可以睡大觉
- 复杂传输场景:分散-聚集(Scatter-Gather)轻松实现
- 极低中断频率:整条链执行完才中断一次
我曾经在工业相机项目中使用链传输,200个描述符串成一链,连续采集10秒图像,CPU只在结束时刻收到一次中断。你说这负载优化得有多痛快?
2.4 三种模式对比与选型
选哪种模式,取决于你的系统对CPU负载和实时性的要求。我通常按这个思路判断:
- 数据量小(< 10字节)、频率低 → 单次传输
- 数据量大、可接受短暂总线延迟 → 块传输
- 数据量大、需要CPU空闲、多个不连续缓冲区 → 链传输
2.5 知识结构图
下面这张图把三种模式的核心关系和适用场景串起来了。
嗯,这张图你看懂了,选型基本不愁。我个人建议,只要硬件支持,优先考虑链传输。它带来的CPU负载降低,是立竿见影的。
避坑提醒:链传输虽然强大,但描述符链必须保证在物理内存中连续(或通过硬件自动取指)。我见过有人把描述符放在堆里,结果DMA读取时内存碎片化导致指针断裂,系统直接挂掉。
2.6 小结
三种模式,从单次到链传输,CPU负载逐步降低,但配置复杂度逐级升高。做项目时,我习惯先看系统对CPU空闲率的要求,再决定模式。如果要求CPU在数据传输期间能干别的事,别犹豫,上链传输。