3、状态保存策略:保存哪些状态,如何最小化保存数据量
状态保存,说白了就是决定「什么东西要记住,什么东西可以扔掉」。
我刚开始做低功耗项目时,踩过一个坑:把所有寄存器一股脑全存了。结果呢?唤醒时间长了,功耗反而没降多少。后来才明白——存得少,才能醒得快。
3.1 三类核心状态:CPU寄存器、外设配置、内存上下文
我们把要保存的状态分成三大类。每一类的保存策略都不一样。
| 状态类别 | 典型内容 | 保存必要性 | 数据量级 |
|---|---|---|---|
| CPU寄存器 | PC、SP、通用寄存器、状态寄存器 | 必须保存,否则程序跑飞 | 几十字节 |
| 外设配置 | GPIO模式、定时器参数、ADC配置、DMA描述符 | 按需保存,部分可重新初始化 | 几百字节到几KB |
| 内存上下文 | 全局变量、堆栈数据、任务控制块 | 按需保存,可压缩或裁剪 | 几KB到几百KB |
核心原则:能重新计算的,就别存。能硬件自动恢复的,就别手动存。
3.2 CPU寄存器:必须存,但可以挑着存
CPU寄存器是程序的「命根子」。不存?醒来后PC指针乱跳,系统直接崩溃。
但这里有个技巧——不是所有寄存器都需要保存。
- PC和SP:必须存。这是程序恢复的锚点。
- 通用寄存器(R0-R12):看情况。如果进入睡眠前已经压栈了,就不需要额外保存。
- 状态寄存器(PSR):必须存。条件跳转全靠它。
- 浮点寄存器:如果没用浮点运算,直接跳过。能省几十字节。
我的习惯:在进入睡眠前,先检查当前上下文。如果是在中断服务程序里进入睡眠,很多寄存器已经被硬件自动压栈了。这时候再手动保存一遍,就是浪费。
3.3 外设配置:能重配就别存
外设配置是状态保存的「大头」。一个UART的配置寄存器可能有十几个,全存下来要上百字节。
我做过一个项目,外设配置占了保存数据量的70%。后来怎么优化的?只保存「不可恢复」的部分。
- GPIO状态:如果唤醒后需要保持输出电平,就存。否则,重新初始化。
- 定时器计数值:如果定时器在睡眠期间继续运行,必须存。否则,清零重来。
- DMA描述符:如果DMA正在传输,必须存。否则,重新配置。
- ADC校准值:有些芯片的ADC校准值存在寄存器里,掉电就丢。这个必须存。
我曾经踩过的坑:某款MCU的RTC配置寄存器在深度睡眠时会复位。我以为它不会丢,结果唤醒后RTC时间全乱了。从那以后,我养成了一个习惯——看数据手册的「复位值」列。凡是复位后不是默认值的,都得存。
3.4 内存上下文:压缩、裁剪、延迟恢复
内存上下文是状态保存的「无底洞」。一个RTOS系统,任务栈、全局变量、堆数据加起来可能几十KB。
怎么最小化?三个思路:
- 压缩存储:用LZSS或RLE算法压缩。我试过,压缩比能达到3:1到5:1。代价是压缩/解压时间,但唤醒时间通常能接受。
- 裁剪无用数据:只保存「活跃」的内存页。比如,只保存当前任务栈,不保存空闲任务栈。
- 延迟恢复:先恢复关键数据(如任务控制块),非关键数据(如日志缓冲区)等系统跑起来后再慢慢恢复。
一个实用的经验值:对于大多数嵌入式系统,状态保存的目标是把数据量控制在1KB以内。超过这个量,唤醒时间就会明显变长。
3.5 状态保存的决策流程
说了这么多,到底怎么决定存什么、不存什么?我画了一张流程图,你看完就明白了。
3.6 实战案例:一个BLE传感器的状态保存
说个具体的例子。我之前做的一个BLE温湿度传感器,电池供电,要求待机电流低于5μA。
状态保存清单是这样的:
| 状态项 | 是否保存 | 原因 | 数据量 |
|---|---|---|---|
| CPU寄存器(PC/SP/PSR) | 是 | 必须存 | 12字节 |
| 通用寄存器(R0-R12) | 否 | 已由硬件自动压栈 | 0字节 |
| BLE协议栈状态 | 是 | 连接信息不可恢复 | 256字节 |
| 传感器校准值 | 是 | 掉电丢失 | 8字节 |
| 定时器配置 | 否 | 唤醒后重新初始化 | 0字节 |
| 任务栈 | 是(压缩后) | 保存当前任务上下文 | 512字节→180字节 |
| 日志缓冲区 | 否 | 延迟恢复 | 0字节 |
总数据量:12 + 256 + 8 + 180 = 456字节。远低于1KB的目标。
一个小技巧:我在这个项目里用了「差分保存」。每次只保存与上次不同的寄存器。比如,外设配置在初始化后基本不变,第一次保存后,后续只保存变化的部分。这样平均数据量又降了30%。
3.7 总结:状态保存的「三要三不要」
- 要:保存CPU关键寄存器(PC/SP/PSR)
- 要:保存不可恢复的外设状态(如DMA描述符、校准值)
- 要:对内存上下文做压缩和裁剪
- 不要:保存可以重新初始化的外设配置
- 不要:保存已经由硬件自动保存的数据
- 不要:保存非关键数据(如日志、调试信息)
嗯,状态保存策略就这些。说白了就是一句话:能不存就不存,非存不可就压缩。