2. DMA控制器原理:双缓冲模式与循环模式在SPI场景下的选择策略

DMA这东西,说白了就是帮CPU干苦力的。SPI通信时,每发一个字节CPU就得去填数据寄存器,每收一个字节又得赶紧去读走——这种"字节搬运"最浪费CPU。我早年做第一版SPI驱动时,CPU主频跑200MHz,结果SPI速率一上10MHz,CPU就被占得死死的,其他任务全卡住。后来上了DMA,整个世界清静了。

但DMA不是一开就完事。它的工作模式选不对,反而会出各种怪问题。今天咱们重点聊两种模式:双缓冲模式和循环模式。在SPI场景下,选哪个?怎么配?我踩过的坑,你最好别踩。

2.1 DMA传输的基本流程

先看一张图,把DMA怎么搬运数据理清楚:

DMA传输基本流程(SPI场景) CPU DMA控制器 SPI外设 内存缓冲区 ① 配置DMA ② 触发传输 ③ 读写数据 ④ 传输完成中断 核心思路:CPU只负责配置和收尾,数据搬运全交给DMA

流程很简单:CPU配好DMA通道,告诉它"从哪搬、搬到哪、搬多少",然后DMA就自己干活了。搬完了发个中断通知CPU。但这里有个关键问题——缓冲区用完了怎么办?

2.2 双缓冲模式(Double Buffer Mode)

双缓冲模式,就是DMA有两块缓冲区,轮流用。一块在传输时,另一块供CPU处理。我习惯叫它"乒乓缓冲"。

工作原理

  • DMA先操作缓冲区A,传输数据
  • 传输完成后,自动切换到缓冲区B
  • 同时触发中断,通知CPU处理缓冲区A的数据
  • CPU处理A时,DMA继续往B搬数据
  • B完成后,再切回A,如此循环

核心优势:CPU和DMA可以并行工作,互不阻塞。数据处理的"流水线"效率极高。

SPI场景适用性

场景 推荐度 原因
SPI从机接收不定长数据 ★★★★★ 双缓冲可以边收边处理,不会丢包
SPI主机发送大块数据 ★★★★☆ CPU可以提前准备下一块数据
SPI全双工通信 ★★★☆☆ 需要同时管理收发两个双缓冲,配置复杂
SPI速率低于1MHz ★★☆☆☆ CPU直接处理就够了,双缓冲反而增加延迟

我的经验:做SPI从机接收传感器数据流时,我必用双缓冲。主机发数据包没有固定间隔,双缓冲能保证CPU任何时候都有完整的数据包可处理,不会因为处理慢了而丢数据。

2.3 循环模式(Circular Mode / Auto-Reload Mode)

循环模式,就是DMA传输完一轮后,自动把地址指针重置到起点,继续下一轮传输。缓冲区是"环形"的,永远不停止。

工作原理

  • DMA配置一个固定大小的环形缓冲区
  • 传输到末尾后,地址自动回绕到开头
  • DMA持续运行,永不停止
  • CPU通过"水位线"或"半满中断"来取数据

注意:循环模式下,DMA不会自动停止。如果CPU取数据的速度跟不上DMA写入的速度,新数据会覆盖旧数据——这就是经典的"数据覆盖"问题。

SPI场景适用性

场景 推荐度 原因
SPI连续数据流采集(如音频、ADC) ★★★★★ 数据源源不断,循环模式最省事
SPI周期性发送固定波形 ★★★★★ 配置一次,DMA自动循环发送
SPI突发性通信 ★☆☆☆☆ 循环模式会浪费中断和带宽
SPI从机接收不定长数据 ★★☆☆☆ 数据边界不好判断,容易覆盖

2.4 两种模式的核心对比

我整理了一张对比表,你选型时直接对着看:

对比维度 双缓冲模式 循环模式
缓冲区数量 2块(A/B) 1块(环形)
自动停止 每块传完自动停 永不停止
中断频率 每块传完一次中断 可配置半满/水位中断
数据覆盖风险 低(有明确边界) 高(CPU处理慢时)
CPU负载 中等(中断+处理) 较低(中断少)
适用数据流 块数据、不定长数据 连续流数据
配置复杂度 中等 简单

2.5 选择策略:什么时候用哪个?

这个问题,我直接给结论:

  1. 数据是"包"还是"流"?
    • 包(有开始有结束)→ 双缓冲
    • 流(源源不断)→ 循环模式
  2. 数据速率高不高?
    • 高(SPI时钟 > 10MHz)→ 双缓冲(防止覆盖)
    • 低(SPI时钟 < 1MHz)→ 循环模式(省中断)
  3. CPU忙不忙?
    • CPU很忙,只能偶尔处理 → 循环模式+大缓冲区
    • CPU有空,需要实时处理 → 双缓冲
  4. 数据边界重要吗?
    • 重要(比如协议帧)→ 双缓冲
    • 不重要(比如连续采样)→ 循环模式

我的选择口诀:"有头有尾用双缓,源源不断用循环;高速怕丢用双缓,CPU太忙用循环。"

2.6 避坑指南

我曾经踩过的坑:做SPI从机接收时,我图省事用了循环模式。结果主机发数据包间隔不固定,CPU处理一个包时,DMA已经把下一个包写进来了——两个包的数据混在一起,解析全乱套。后来换成双缓冲,每个包都有完整的缓冲区,问题立刻解决。

还有几个常见问题:

  • 双缓冲切换时丢数据?检查一下切换中断的响应时间。如果CPU关中断太久,DMA可能已经覆盖了缓冲区。我一般把DMA中断优先级设到最高。
  • 循环模式半满中断不准?有些MCU的半满中断是在"半满"时触发一次,不是持续触发。需要配合DMA传输计数寄存器来判断实际水位。
  • 缓冲区大小怎么定?双缓冲建议每块至少能装2-3个数据包;循环模式建议缓冲区大小是CPU处理周期的2倍以上,留出余量。

2.7 小结

双缓冲和循环模式,没有绝对的好坏。关键看你的SPI场景是"块传输"还是"流传输"。我个人的习惯是:不确定时先用双缓冲,因为它更安全、边界更清晰。等系统稳定了,再根据性能需求考虑是否换成循环模式。