4. DMA 传输带宽计算:单次传输大小、传输周期、带宽公式与实测差异
带宽这东西,说白了就是「单位时间能搬多少数据」。
DMA 的带宽不是你拍脑袋定的,它和 SPI 时钟频率、传输模式、系统总线负载都绑在一起。我在项目里见过有人 SPI 时钟配了 80 MHz,DMA 却只能跑 30 MB/s,一查才发现是传输周期算错了。嗯,今天咱们就把这笔账算清楚。
4.1 单次传输大小
DMA 每次触发,搬多少数据?这由两个参数决定:
- 数据宽度:8 位、16 位、32 位(常见于 STM32 等 MCU)
- 突发传输长度:1、4、8、16 次(取决于 DMA 控制器)
你想想看,单次传输大小 = 数据宽度 × 突发长度。比如用 32 位宽度 + 突发 4 次,就是 16 字节一次。但这里有个坑:很多 SPI 外设只支持 8 位或 16 位 FIFO,你配了 32 位宽度,实际可能被截断或对齐出错。我遇到过用 32 位 DMA 写 SPI 寄存器,结果高位数据丢了,排查了半天才意识到是硬件限制。
4.2 传输周期
一次 DMA 传输需要多少个时钟周期?它包括三个阶段:
- 仲裁等待:总线被其他主控占用,DMA 等着。
- 源地址读取:从存储器或外设取数据。
- 目标地址写入:写到 SPI 发送寄存器(或内存)。
大多数 MCU 的 DMA 是单周期传输的,即 1 个总线时钟完成一次搬移。但如果是非对齐地址,或跨了 AHB 桥,可能插入等待周期。我在一次调试中,DMA 把数据从 SRAM 搬到 SPI,结果 SPI 时钟 40 MHz,DMA 却只能跑到 20 MB/s,原因就是 SRAM 和 SPI 不在同一个总线矩阵上,多了两个等待周期。所以,传输周期 ≈ 1 个总线时钟 × (1 + 等待因子)。
4.3 带宽公式
理论带宽 = (单次传输大小) / (传输周期) 。用总线时钟频率换算一下:
// 假设:
// 总线时钟 = 80 MHz
// 单次传输大小 = 1 字节(8 位)
// 传输周期 = 1 个总线时钟(无等待)
理论带宽 = 80 MHz × 1 字节 = 80 MB/s
// 但 SPI 是有时钟的,实际受限于 SPI 时钟。
// 如果 SPI 时钟 = 40 MHz,全双工一次可收+发 1 字节,
// 则 DMA 理论传输带宽 = 40 MHz × 1 字节 = 40 MB/s
明白了?带宽要取总线侧和 SPI 侧的较小值。我见过有人用 80 MHz 总线时钟去算 DMA 带宽,觉得能跑 80 MB/s,结果 SPI 时钟只有 10 MHz,DMA 再快也只能干等着。说白了,瓶颈在慢的那一端。
| 参数 | 典型值 | 影响带宽 |
|---|---|---|
| 总线时钟 | 80 MHz | 决定了 DMA 最大吞吐 |
| SPI 时钟 | 40 MHz | 直接限制传输速率 |
| 数据宽度 | 8 / 16 / 32 位 | 每次传输的字节数 |
| 等待周期 | 0~3 个时钟 | 增加传输时间,降低有效带宽 |
4.4 实测差异
理论公式算出来漂亮,实际一测,常常打八折。为什么会这样?
- DMA 请求间隔:SPI 发送完一个字节后,才会触发下次 DMA 请求。如果 SPI 自身 FIFO 深度小(比如 1 个字节),DMA 就被 SPI 的节奏拖着走,带宽自然上不去。
- 总线仲裁冲突:CPU 或其他外设也在抢总线,DMA 会等。实测时把其他中断关掉,带宽能提升 10~20%。
- DMA 初始化开销:有些 DMA 需要重新配置源/目标地址,这部分时间没算进传输周期。
- 缓存一致性问题:如果你用了带 Cache 的 MCU,DMA 读到的是旧数据,必须手动 clean/invalidate,这部分软件开销也会吃掉带宽。
做个实验:用逻辑分析仪抓 SPI 的 SCK 和 DMA 的请求信号,看看实际 SCK 的脉冲间隔。如果间隔比理论值大,说明 DMA 在等总线。我习惯在代码里加一个 GPIO 翻转来标记 DMA 完成,用示波器量——这比算半天都准。
实际带宽 ≈ min(总线带宽, SPI 带宽) × (1 - 总线冲突率) × (1 - 等待开销)
如果你能保证 SPI FIFO 深度 ≥ 2,且 DMA 与 CPU 分时使用总线,实测一般能到理论值的 80%~90%。