GD32 ACM核 MCU进入低功耗模式,唤醒后需要注意的事,程序如何正常运行??
非常好的问题 👏👏
你这段总结非常准确,
我们就基于 GD32F4 的 Deep Sleep 模式(Cortex-M4 的 SLEEPDEEP) 来一步步“解读 + 对应你程序实际要做的事”。
🧠 一、先明确:Deep Sleep ≠ 复位
在 Deep Sleep 模式 下:
- CPU 停止运行;
- SRAM 和寄存器内容保留;
- 唤醒后 程序继续从 WFI 指令后面执行。
也就是说,你的 enter_pd_mode()
调用:
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_ENABLE, WFI_CMD);
唤醒后会从这行往下继续执行。
程序不会从 main()
或 Reset_Handler
重新启动。
⚙️ 二、逐条解读你贴的官方描述
① “关闭1.2V域所有时钟,禁用IRC16M、HXTAL及PLL,但SRAM和寄存器内容保留。”
意思是:
模块 | 唤醒前状态 | 进入DeepSleep | 唤醒后 |
---|---|---|---|
PLL/HXTAL(主时钟源) | 开启 | 被硬件关闭 | ❌ 不自动恢复 |
IRC16M(内部RC振荡器) | 开启 | 被关闭 | ⚠️ 唤醒后自动恢复并作为系统时钟 |
SRAM | 保留 | 保留 | 保留 |
外设寄存器 | 大部分保留 | 保留 | 但时钟未开,所以失效 |
👉 所以你唤醒后虽然“代码接着跑”,但所有外设(USART、SPI、ADC 等)时钟源都挂了。
此时系统默认只靠 IRC16M(16 MHz)在跑。
② “1.2V 域供电正常,LDO可配置为正常功耗或低功耗模式”
- 1.2V 域 = CPU 和核心逻辑域。
- 低功耗模式下,LDO 会“缓慢供电”,功耗更低,但唤醒时需要时间让电压稳定。
也就是说:
如果你在进入低功耗时使用了
PMU_LDO_LOWPOWER
(低功耗LDO),
那唤醒时 MCU 不能立刻执行高速指令,
要“等几微秒”让内部 LDO 稳定后才能恢复正常速度。
这就对应到:
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_ENABLE, WFI_CMD);
你这里用的 PMU_LDO_LOWPOWER
→ 意味着唤醒后要多等待一点时间再跑高速代码。
③ “退出后时钟:默认恢复IRC16M作为系统时钟”
这非常关键 ⚠️
MCU唤醒后系统时钟 = 内部RC振荡器IRC16M(16 MHz)
也就是说:
- 你进入DeepSleep之前,主频可能是 240 MHz(PLL);
- 唤醒后,主频只有 16 MHz;
- 所以所有基于时钟的外设(UART波特率、定时器、SPI速率)全都错了!
🔎 三、结合你的代码解释唤醒后现象
你的 UART 初始化时是基于 240 MHz 系统时钟:
usart_baudrate_set(USART1, 115200);
但唤醒后,系统自动退回到 16 MHz,而你没有重新配置 PLL。
结果就是:
串口波特率变了 ≈ 原来的 1/15,
电脑看到的是乱码或完全没输出。
✅ 四、唤醒后应该做的三件事
步骤 | 动作 | 原因 |
---|---|---|
1️⃣ | 等待 LDO 稳定 | 如果用低功耗模式进入,要等待电源恢复稳定 |
2️⃣ | 重新配置系统时钟(PLL/HXTAL) | 唤醒后系统默认是 IRC16M,需要恢复原时钟 |
3️⃣ | 重新初始化外设(UART/SPI/ADC等) | 因为波特率/分频器依赖系统时钟 |
示例修复流程(GD32 官方推荐)
void enter_pd_mode(void)
{debug("Enter DeepSleep...\r\n");wakeup_gpio_init();// 进入低功耗模式pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_ENABLE, WFI_CMD);// === 唤醒后执行此处 ===// 1. 等待LDO稳定(关键!)for (volatile int i = 0; i < 5000; i++); // 简单延时几百微秒// 2. 重新配置系统时钟system_clock_config(); // 恢复到原来的240MHz主频// 3. 重新初始化外设uart1_init();adc_init();spi_init();debug("Wakeup OK, system clock restored to 240MHz!\r\n");
}
💡 五、额外说明:如果你不恢复PLL也可以打印
假设你想快速验证程序“确实唤醒了”而不恢复PLL,可以:
- 暂时用 IRC16M 重新初始化UART;
- 用较低波特率,如 9600;
- 这样在唤醒后不改时钟也能看到打印。
usart_baudrate_set(USART1, 9600);
但正式程序要恢复PLL,不然系统跑在16MHz性能太低。
🧾 六、总结一句话
项目 | 说明 | 对应代码动作 |
---|---|---|
⚙️ 程序位置 | 从WFI下一行继续执行 | 无需特殊处理 |
⚡ LDO唤醒时间 | 如果进入低功耗LDO,要加延时 | 延时几百微秒再重启外设 |
🕓 时钟源变化 | 唤醒后默认 IRC16M | 重新配置 system_clock_config() |
📡 串口失效 | 因波特率基于系统时钟 | 重新初始化UART |
你要不要我帮你在你现有的 enter_pd_mode()
里,插入一段“带时钟恢复 + LDO稳定等待 + 串口恢复”的完整版本?
我可以直接给你一份能在 GD32F4 上跑通、唤醒后立即恢复打印的参考代码。
这个问题非常关键 👍,我们来结合你截图里的 GD32F4xx《用户手册》表 3.1(节电模式总结),
详细对比一下三种低功耗模式(睡眠 / 深度睡眠 / 待机),然后告诉你该选哪个最合适。
🧭 一、三种模式对比总结表
模式 | 睡眠模式 | 深度睡眠模式(Deep Sleep) | 待机模式(Standby) |
---|---|---|---|
核心状态 | CPU停止(其他外设继续工作) | 关闭1.2V域时钟,CPU停止 | 关闭1.2V电源域(几乎全关机) |
供电 | 全部正常供电 | 1.2V域供电保持,外设大多停 | 仅RTC和唤醒引脚供电 |
寄存器 & SRAM | 全部保留 | 全部保留 | 全部丢失(复位重新启动) |
唤醒源 | 任意中断或事件 | EXTI中断/事件(GPIO、RTC等) | RTC闹钟、WKUP脚、NRST、FWDGT |
唤醒后执行位置 | WFI下一条指令 | WFI下一条指令 | 从复位重新执行(Reset_Handler) |
系统时钟恢复 | 不变 | 唤醒后IRC16M默认启用(需重新配置PLL) | 系统完全复位,重新初始化 |
唤醒延迟 | 极短 | 有(IRC16M + LDO稳定) | 最长(系统复位启动) |
功耗 | 低 | 更低 | 最低 |
⚙️ 二、三者的使用场景建议
场景 | 推荐模式 | 说明 |
---|---|---|
🧩 CPU空闲但仍要快速响应中断 | 睡眠模式 | 适合RTOS下的 __WFI() 调度 |
⚡ 需要“保持内存内容”,但长时间低功耗 | ✅ 深度睡眠模式(Deep Sleep) | 唤醒快、状态保持,是最常用的中功耗休眠 |
🔋 要求极限低功耗(掉电保存RTC即可) | ⚠️ 待机模式(Standby) | 唤醒等于重新上电,需重初始化系统 |
🧠 三、重点分析:
你当前是做 无人机飞控系统(GD32F4xx, FreeRTOS/裸机),所以:
- 你进入低功耗的目的是“节能待机,但保持飞控状态或内存参数”,
- 唤醒后还要能继续执行飞控逻辑(不用重新启动主程序)。
👉 所以:
✅ 选:深度睡眠模式(Deep Sleep) 是最佳方案。
理由如下:
项目 | Deep Sleep 优点 |
---|---|
SRAM & 寄存器保留 | 唤醒后程序可继续执行,不丢任务状态 |
外设关闭省电 | 可大幅降低功耗 |
唤醒快 | 比待机模式快几十倍(ms级 vs µs级) |
唤醒后恢复灵活 | 可重新打开PLL、外设,自定义恢复速度 |
对飞控安全 | 不会因为待机丢寄存器造成姿态丢失 |
⚠️ 四、什么时候才用 Standby(待机模式)
只有在以下几种情况才考虑:
- 设备长时间闲置(几小时以上不工作);
- 唤醒后系统完全重启也没关系;
- 不需要保存RAM里的飞控状态;
- 需要靠 RTC闹钟 / WKUP引脚 唤醒。
例如:
无人机完全关机状态(只保持RTC计时),
再次上电等于重新开机。
✅ 五、结论
项目 | 选择 |
---|---|
你的场景(飞控休眠、RAM保持、快速唤醒) | 深度睡眠模式 ✅ |
想彻底掉电、超低功耗、重启启动 | 待机模式 ⚠️ |
💡 附:进入和唤醒的推荐写法
void enter_deep_sleep_mode(void)
{debug("Enter DeepSleep...\r\n");/* 使能唤醒引脚或中断 */exti_init_for_wakeup();/* 清除中断标志等 */exti_interrupt_flag_clear(WAKEUP_EXTI_LINE);/* 进入深度睡眠,使用低功耗LDO */pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_ENABLE, WFI_CMD);/* === 唤醒后 === *//* 等待LDO稳定 */for (volatile int i = 0; i < 5000; i++);/* 恢复系统时钟 */system_clock_config();/* 恢复外设 */uart_init();sensor_reinit();debug("Wakeup from DeepSleep OK!\r\n");
}
如果你告诉我:
- 你希望进入低功耗的时间(比如几秒几分钟),
- 唤醒方式(RTC闹钟、外部按键、EXTI中断),
我可以帮你写出完整的「深度睡眠+唤醒+时钟恢复」模板。
是否要我帮你写这一版?