1. 课程导论:DMA与低功耗的“爱恨情仇”——为什么需要协同设计?

各位工程师朋友,欢迎来到这门课。

咱们开门见山。DMA(直接存储器访问)和低功耗设计,这两个东西单独拎出来,大家都不陌生。但把它们放在一起,很多人就开始头疼了。我见过不少项目,DMA用得挺溜,一进低功耗模式就“死机”;也见过为了省电,把DMA功能砍掉大半,结果CPU累得半死,功耗反而没降下来。

这俩家伙,到底是朋友还是敌人?

1.1 先聊聊DMA:它到底在忙什么?

DMA,说白了就是一个“数据搬运工”。CPU让它搬数据,它就去搬,搬完了告诉CPU一声。CPU呢?该睡觉睡觉,该算算术算算术,不用管这些琐事。

我举个例子。你做一个ADC连续采样,每秒要采集10000个数据。如果没有DMA,CPU每采一个数就得进一次中断,把数据从ADC寄存器搬到内存里。10000次中断,CPU基本就废了,啥也别干了。

有了DMA呢?你配置好源地址(ADC数据寄存器)、目的地址(内存数组)、传输长度(10000个),然后启动。DMA自己一趟一趟地搬,CPU全程可以睡大觉,或者干别的活。等DMA搬完了,给你一个完成中断,你一次性处理这10000个数据。

核心价值: DMA让CPU从“数据搬运工”变成“数据管理者”,把宝贵的MIPS(每秒百万指令)用在刀刃上。

1.2 再聊聊低功耗:STM32的“省电秘籍”

STM32的低功耗模式,大家应该都接触过。从最简单的Sleep模式,到深度睡眠的Stop模式,再到几乎掉电的Standby模式,省电效果依次递增,但唤醒代价也越来越大。

我整理了一个表格,方便大家对比:

模式 典型电流 CPU状态 外设状态 唤醒时间
Run(运行) 几十~几百mA 全速运行 全部可用 —
Sleep(睡眠) 几~几十mA 暂停,可被中断唤醒 全部可用(时钟继续) 几us
Stop(停止) 几十~几百uA 暂停,SRAM保持 部分可选保持 几十us
Standby(待机) 几uA以下 掉电,SRAM丢失 仅RTC、IWDT等 几百us~几ms

嗯,这里要注意。很多工程师觉得,只要进了Stop模式,功耗就万事大吉了。但实际情况是,你如果没处理好DMA和外设的时钟关系,系统可能根本进不了Stop模式,或者进去了,DMA还在偷偷耗电。

1.3 冲突点:DMA和低功耗的“爱恨情仇”

问题来了。DMA要工作,需要时钟。低功耗模式要省电,第一件事就是关时钟。这不就冲突了吗?

我总结了一下,常见的冲突点有三个:

  1. 时钟依赖冲突: DMA控制器通常挂在AHB总线上。进入Stop模式时,AHB总线时钟会被关闭。如果DMA正在传输,突然断时钟,轻则传输错误,重则总线挂死。
  2. 请求源失效: DMA传输需要外设触发(比如ADC、USART、SPI)。进入低功耗模式后,这些外设的时钟可能被关闭,触发信号没了,DMA就傻等着。
  3. 唤醒后状态恢复: 从Stop模式唤醒后,DMA控制器的寄存器内容是否保持?有些系列是保持的,有些需要重新配置。我就在F4系列上踩过坑——唤醒后DMA的传输地址指针乱了,数据全写错地方。
避坑指南: 我曾经在一个电池供电的传感器项目中,为了省电,让系统每100ms进入Stop模式。结果发现,每次唤醒后,DMA传输的第一个数据总是错的。查了三天,最后发现是DMA的FIFO在Stop模式下被清空了,而我没有重新初始化。从那以后,我养成了一个习惯:所有涉及DMA的低功耗切换,必须做完整的“进入前保存”和“唤醒后恢复”流程。

1.4 协同设计:不是二选一,而是“既要又要”

所以,DMA和低功耗不是敌人。它们是合作伙伴。你需要做的,是学会如何让它们协同工作。

协同设计的核心思路,我画了一张图:

DMA与低功耗协同设计核心逻辑 DMA传输阶段 CPU进入Sleep模式 DMA独立完成数据搬运 外设时钟保持开启 传输完成后触发中断 低功耗阶段 DMA传输全部完成 关闭外设时钟 系统进入Stop/Standby 保留DMA配置寄存器 传输完成 唤醒恢复阶段 恢复外设时钟 → 重新使能DMA时钟 检查DMA寄存器状态 → 恢复传输指针 重新配置DMA通道(如需)→ 继续传输 唤醒事件 继续下一轮传输

说白了,协同设计的精髓就是:在DMA干活的时候,让CPU睡;在DMA干完活之后,让整个系统睡。 这样既保证了数据吞吐,又实现了极致省电。

1.5 这门课能给你什么?

我设计这门课,就是要把这些“坑”和“招”系统地讲清楚。咱们不搞虚的,直接上实战。

  • 你会学到: 如何在STM32F1/F4/L4/G0等主流系列上,配置DMA与低功耗模式的协同工作。
  • 你会看到: 完整的代码示例,从初始化到低功耗切换,再到唤醒恢复,每一步都有注释。
  • 你会避开: 我这些年踩过的坑——比如DMA FIFO在Stop模式下的行为、不同系列DMA寄存器保持特性的差异、以及如何用DMA+定时器实现“无CPU干预”的周期性采样。
个人建议: 如果你现在手头有STM32开发板,建议先跑一个最简单的DMA传输例程(比如DMA搬运内存数据),然后用调试器观察进入Stop模式前后的功耗变化。你会发现,即使DMA没在工作,只要它的时钟没关,功耗就会多出几十uA。这就是我们后面要优化的重点。