ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

RP2040底层低功耗解析:寄存器级睡眠模式与唤醒机制

2026/9/11 15:42:11 拓冰建站 浏览量
RP2040底层低功耗解析:寄存器级睡眠模式与唤醒机制 1. 这不是“省电开关”而是芯片级的呼吸控制术你手里的那块树莓派Pico表面看就是一块带USB接口的蓝色小板子插上电脑自动识别成U盘拖个.uf2文件就跑起来——但它的底层藏着一套精密到毫秒级的能耗调度系统。很多人把“低功耗模式”理解成关掉几个外设、调低主频、再进个sleep()函数就完事了这就像说“开车省油松油门”忽略了变速箱逻辑、气门正时、甚至轮胎滚阻这些真正决定油耗的底层变量。RP2040的低功耗不是靠软件“喊停”而是靠硬件寄存器直接改写芯片内部状态机的运行路径让CPU、总线、外设控制器、甚至片上SRAM的供电域逐级断电、时钟门控、电压域切换——整个过程像指挥一支训练有素的军队谁撤出前线、谁进入警戒、谁原地休整、谁保持待命全由几组32位寄存器的一次写操作精准裁定。我第一次在示波器上看到Pico从运行态跳入deep sleep模式时VDD_IO电压曲线的陡降电流从25mA瞬间跌到23μA那种“咔哒”一声物理级断电感比任何文档描述都来得真实。这不是软件延时是硬件状态机的硬切换。而触发这一切的往往只是对PM_CSRPower Management Control and Status Register中某一位的置位操作。RP2040没有传统意义上的“电源管理单元PMU”它的低功耗能力全部内嵌在SYSCTRL和XOSC等核心外设控制器里靠的是对时钟源、复位信号、电压域、唤醒源这四大要素的协同裁决。所以“配置寄存器”不是填空题而是解一道多约束方程你改了CLK_SYS_CTRL里的分频系数就得同步检查RESETS里对应外设是否被意外复位你关闭了USB PHY的时钟就得确认IO_BANK0的GPIO唤醒功能是否还有效你设定了WAKE_EN寄存器的唤醒引脚就得确保PADS_BANK0里该引脚的上拉/下拉电阻配置不会导致漏电——任何一个环节的疏忽都会让芯片卡在某个中间态既不运行也不真睡电流悬停在毫安级成了名副其实的“假眠”。这个项目标题里的“底层解析”指的就是把这套硬件状态流转的因果链彻底拆开从CM4内核的WFI/WFE指令如何触发系统级低功耗请求到SYSCTRL模块如何解析该请求并生成内部断电信号再到XOSC振荡器如何响应断电指令进入低频维持模式最后到ROSCRing Oscillator如何作为深睡期间唯一的时钟源支撑RTC计时与唤醒检测。它不讲HAL库API怎么用只讲你写*(uint32_t*)0x4000c000 0x10000000;这一行代码时背后发生了多少晶体管的导通与截止多少微安电流的启停多少纳秒级的信号延迟。如果你的目标是让一块Pico在纽扣电池上撑过一年或者让一个环境传感器节点每小时只醒100ms采集温湿度再沉睡那么你必须亲手摸清这些寄存器地址、位域定义、时序约束和状态依赖——因为库函数封装的永远是通用解而你的场景需要的是唯一最优解。2. 低功耗模式的本质四层状态塔与三类唤醒源RP2040的低功耗体系不是简单的“开/关”二元结构而是一座由四层状态构成的垂直塔每一层都对应着不同粒度的资源裁剪和能耗水平。理解这座塔的层级关系是避免配置错误的第一道门槛。很多初学者一上来就冲着“deep sleep”去结果发现唤醒失败或电流居高不下问题往往出在没搞清自己究竟站在哪一层以及如何安全地上下楼。2.1 四层状态塔从轻量休眠到深度断电第一层Run Mode运行模式这是默认状态所有时钟全开CPU执行指令所有外设可访问。电流消耗取决于主频、外设使能数、GPIO负载等。典型值7–25mA133MHz主频USBUARTSPI全开。第二层Idle Mode空闲模式这是标题里提到的热搜词“idle低功耗休眠模式”。它本质是CPU内核级暂停但系统时钟、总线、外设控制器仍保持供电。触发方式是CM4内核执行WFIWait For Interrupt指令。此时CPU停止取指但中断控制器、DMA、GPIO、RTC等外设仍在后台运行。关键点在于所有外设时钟必须保持使能否则中断可能丢失。例如若你关闭了CLK_PERI那么GPIO中断将无法唤醒CPU。电流典型值3–8mA降幅约60%但实现最简单适合短时等待如串口接收等待。第三层Sleep Mode睡眠模式这是真正的系统级低功耗。它要求CPU、总线矩阵、大部分外设控制器进入断电状态仅保留少数关键模块供电。触发条件是向PM_CSR寄存器的SLEEP_EN位写1并确保SLEEP_HIBERNATE位为0。此时CPU内核完全断电系统总线AHB/APB时钟门控关闭XOSC主晶振可选择性关闭需提前配置XOSC_CTRL的ENABLE位ROSC环形振荡器必须保持运行作为唤醒时钟源RTC实时时钟继续计时配置好的GPIO引脚通过WAKE_EN可触发唤醒 电流典型值0.5–2mA降幅达90%以上。这是平衡功耗与唤醒速度的常用选择唤醒延迟约10–50μs。第四层Deep Sleep Mode深度睡眠模式这是功耗最低的状态目标是让芯片接近“物理静止”。触发条件是向PM_CSR的SLEEP_HIBERNATE位写1。此时XOSC主晶振强制关闭ROSC成为唯一时钟源且其频率被降至最低约1MHz片上SRAM包括RAM0/RAM1可选择性掉电通过PM_SRAM_CTRL寄存器控制所有IO Bank的电源域可部分关闭需配置PADS_BANK0的IO_VDD控制位唯一能唤醒的源是RTC闹钟或配置好的GPIO边沿 电流可压至23–50μA典型值降幅超99%。但代价是唤醒延迟显著增加1–5ms且唤醒后需重新初始化XOSC导致系统启动时间变长。提示RP2040的“Hibernate”并非传统意义的掉电保存它不支持SRAM内容保持除非外接备用电池。所谓“Deep Sleep”本质是“Clock Gated Deep Sleep”即通过极致的时钟门控和电源域管理实现超低功耗而非真正断电。2.2 三类唤醒源谁有权叫醒沉睡的芯片低功耗模式的价值一半在“睡”一半在“醒”。RP2040提供了三类独立且可组合的唤醒机制它们的配置分散在不同寄存器组必须协同设置1. GPIO唤醒最常用原理将特定GPIO引脚配置为输入并启用其边沿检测上升沿/下降沿/双边沿当检测到有效电平变化时产生中断请求唤醒系统。关键寄存器WAKE_EN0x4000c01032位每位对应一个GPIObit0GPIO0, bit31GPIO31置1表示该引脚使能唤醒。WAKE_LOW0x4000c014与WAKE_HIGH0x4000c018分别定义哪些引脚在低电平/高电平状态下触发唤醒注意这是电平检测非边沿边沿检测需配合GPIO中断配置。IO_BANK0_GPIOx_CTRL0x40014000 x*0x08配置GPIO功能为SIOSystem IO并设置IRQOVER位以启用中断。注意GPIO唤醒必须配合PADS_BANK0的引脚配置。例如若GPIO2用于唤醒需确保PADS_BANK0_GPIO2寄存器中的PULLUP/PULLDOWN位设置正确否则浮空引脚会因噪声误触发。我曾因忘记配置上拉电阻导致一块部署在野外的传感器每分钟被风扰动唤醒一次电池三天耗尽。2. RTC唤醒定时唤醒原理利用片内实时时钟RTC的闹钟功能在设定时间点产生中断唤醒。这是实现周期性采样的核心。关键寄存器RTC_CS0x4005c000RTC控制状态寄存器EN位使能RTC。RTC_DIV0x4005c004分频系数决定RTC时钟源ROSC的最终频率如DIV0x100000≈ 1Hz。RTC_ALARM0x4005c008闹钟匹配值当RTC计数值等于此值时触发中断。RTC_INTR0x4005c010中断状态寄存器ALARM位指示闹钟触发。实操心得RTC闹钟是“一次性”的。唤醒后必须手动清除RTC_INTR的ALARM位并重新写入新的RTC_ALARM值否则下次不会再次触发。很多初学者卡在这里以为设了一次闹钟就能循环唤醒结果只醒一次就再无动静。3. USB唤醒特殊场景原理当USB设备处于挂起状态Suspend主机发送远程唤醒信号Resume Signaling时RP2040可被唤醒。这需要USB PHY保持部分供电。关键寄存器USBCTRL_DPR0x40050000USB Device Power RegisterUSB_EN位控制USB PHY供电。USBCTRL_SIE_STATUS0x40050004SIE状态寄存器RESUME位指示唤醒事件。注意USB唤醒仅在USB设备模式下有效且需主机端支持远程唤醒。普通开发中极少使用但在USB HID键盘/鼠标等需要即时响应的场景中不可或缺。3. 寄存器配置实战从理论地址到稳定运行的七步法理解状态塔和唤醒源只是蓝图真正让Pico进入可控低功耗的是那一行行对特定内存地址的读写操作。RP2040的寄存器映射遵循ARM Cortex-M4标准所有外设寄存器基地址均位于0x40000000起始的APB总线空间。但“知道地址”不等于“能用好”实际配置中存在大量隐含约束和时序陷阱。以下是我经过数十次实测验证的七步法每一步都对应一个关键决策点缺一不可。3.1 第一步确认当前时钟源与分频状态低功耗配置前必须明确系统当前的时钟拓扑。RP2040的时钟树极其灵活但低功耗模式对时钟源有严格要求。例如进入Deep Sleep前XOSC必须被关闭而ROSC必须保持运行但若ROSC本身被意外禁用整个系统将无法唤醒。// 检查XOSC是否已启用关键 uint32_t xosc_ctrl *(volatile uint32_t*)0x40024000; // XOSC_CTRL if (!(xosc_ctrl (1 0))) { // XOSC未启用需先启动 *(volatile uint32_t*)0x40024000 0x1; // 写ENABLE1 // 等待稳定官方手册要求至少1ms for(volatile int i0; i100000; i); }为什么这一步不能省因为很多开发板尤其是国产兼容板的XOSC在出厂固件中可能被禁用或者用户代码在初始化阶段错误地关闭了它。直接进入Deep Sleep会导致ROSC成为唯一时钟源但若ROSC也未启用芯片将彻底失联。我曾用示波器抓取XOSC引脚波形发现一块新购Pico的晶振根本没起振根源就是Boot ROM未执行XOSC使能序列。3.2 第二步配置唤醒源并清除待处理中断唤醒源必须在进入低功耗前完成初始化且所有可能干扰唤醒的“脏中断”必须清除。否则一个未处理的UART接收中断可能在你写入WFI指令的瞬间抢占CPU导致永远无法进入休眠。// 1. 使能GPIO2作为唤醒源 *(volatile uint32_t*)0x4000c010 | (1 2); // WAKE_EN[2] 1 // 2. 配置GPIO2为输入启用上拉防误触发 *(volatile uint32_t*)0x40014010 0x00000000; // GPIO2_CTRL: FUNCSEL0 (SIO) *(volatile uint32_t*)0x4001c008 | (1 1); // PADS_BANK0_GPIO2: PULLUP1 // 3. 清除所有待处理的GPIO中断关键 *(volatile uint32_t*)0x40014014 0xffffffff; // GPIO2_INTe: 写1清中断标志 *(volatile uint32_t*)0x40014018 0xffffffff; // GPIO2_INTf: 写1清中断标志 // 4. 全局使能中断NVIC *(volatile uint32_t*)0xe000e100 0x00000001; // NVIC_ISER0, 使能IRQ0 (GPIO_IRQ0)实操心得“清除中断标志”这一步极易被忽略。RP2040的GPIO中断标志是“写1清零”且必须对INTe边缘中断和INTf电平中断两个寄存器分别操作。如果不清除即使你没配置GPIO中断服务函数只要引脚电平变化过中断标志位就一直为1WFI指令一执行CPU立刻被唤醒形成“刚睡着就弹起”的死循环。3.3 第三步选择低功耗模式并写入控制寄存器这是核心动作直接决定芯片进入哪一层状态塔。PM_CSR0x4000c000是权力中心其各位定义如下位名称功能低功耗模式要求0SLEEP_EN使能Sleep模式Sleep/Deep Sleep必需1SLEEP_HIBERNATE使能Deep SleepDeep Sleep必需Sleep模式下必须为02WAKE_STICKY唤醒后保持唤醒状态通常置0避免误唤醒3WAKE_FORCE强制唤醒调试用生产环境置0// 进入Sleep Mode非Deep Sleep uint32_t pm_csr *(volatile uint32_t*)0x4000c000; pm_csr ~(1 1); // 清除SLEEP_HIBERNATE pm_csr | (1 0); // 设置SLEEP_EN *(volatile uint32_t*)0x4000c000 pm_csr; // 进入Deep Sleep Mode pm_csr | (1 1); // 设置SLEEP_HIBERNATE *(volatile uint32_t*)0x4000c000 pm_csr;关键细节PM_CSR的写入是“触发式”的即写入后立即生效无需额外指令。但必须确保写入前所有唤醒源已就绪否则芯片可能进入不可预测状态。我建议在写入PM_CSR前用__DSB()Data Synchronization Barrier指令确保所有之前的内存写操作已完成避免流水线乱序导致配置未生效就进入休眠。3.4 第四步执行WFI或WFE指令并等待唤醒寄存器配置完毕后CPU需执行一条“等待指令”来正式进入低功耗。RP2040支持两条指令WFIWait For Interrupt等待任意中断包括SysTick、GPIO、RTC等。WFEWait For Event等待事件Event通常由SEV指令触发多用于多核同步在单核Pico中较少用。// 标准流程配置完成后执行WFI __DSB(); // 数据屏障确保配置写入完成 __WFI(); // 执行WFICPU暂停系统进入低功耗 // 唤醒后程序从此处继续执行注意WFI指令本身不耗电但它依赖于中断控制器的正确配置。如果之前步骤中NVIC未使能对应中断或者中断优先级被屏蔽WFI将无限等待程序“卡死”。因此务必在WFI前用NVIC_GetActive(IRQn)检查中断是否已激活。3.5 第五步唤醒后状态恢复与外设重初始化从低功耗唤醒不是“无缝续播”而是一次微型重启。XOSC在Deep Sleep中被关闭唤醒后需重新启动并等待稳定ROSC频率在Deep Sleep中被降低需恢复部分外设如ADC、SPI的配置寄存器可能因断电而丢失需重写。// Deep Sleep唤醒后必须执行的恢复流程 // 1. 重启XOSC *(volatile uint32_t*)0x40024000 0x1; // ENABLE1 for(volatile int i0; i100000; i); // 等待稳定 // 2. 切换系统时钟源回XOSC *(volatile uint32_t*)0x40028000 0x00000001; // CLK_SYS_CTRL: SRCXOSC // 3. 重置并重新配置关键外设如UART *(volatile uint32_t*)0x4002c000 0x1; // UART0_CTRL: RESET1 *(volatile uint32_t*)0x4002c000 0x0; // 清除RESET // ... 重新写入BAUD, CS等寄存器实操心得很多开发者以为唤醒后代码自动继续忽略了时钟源切换的必要性。我曾遇到一个现象Deep Sleep唤醒后UART打印乱码波特率完全不准。用逻辑分析仪抓取CLK_SYS信号发现唤醒后系统时钟源仍是ROSC1MHz而非预期的XOSC12MHz导致UART分频计算错误。根源就是忘了在唤醒后执行时钟源切换。3.6 第六步SRAM掉电控制Deep Sleep专属在Deep Sleep模式下为追求极致功耗可选择让片上SRAM掉电。RP2040有两块SRAMRAM0128KB和RAM116KB。PM_SRAM_CTRL0x4000c020寄存器控制其供电位名称功能0RAM0_POWER_DOWNRAM0掉电1掉电1RAM1_POWER_DOWNRAM1掉电1掉电2RAM0_RETENTIONRAM0保持内容1保持需RAM0_POWER_DOWN03RAM1_RETENTIONRAM1保持内容1保持需RAM1_POWER_DOWN0// Deep Sleep前关闭RAM0供电假设数据已保存到Flash或外部存储 *(volatile uint32_t*)0x4000c020 | (1 0); // RAM0_POWER_DOWN 1 // 唤醒后若需RAM0内容必须重新初始化因内容已丢失 // 此时需从备份区域如XIP Flash重新加载关键变量风险提示SRAM掉电意味着所有全局变量、堆栈内容将丢失。除非你实现了完整的上下文保存/恢复机制如将关键变量复制到保留内存或外部Flash否则不要轻易开启此选项。我见过太多项目因开启RAM0_POWER_DOWN后未做恢复导致唤醒后程序崩溃。3.7 第七步功耗验证与电流校准配置完成不等于成功。必须用实测电流验证是否达到预期。推荐方法断开USB数据线仅保留VBUS供电USB数据通信会引入额外电流约1–2mA影响测量精度。使用uA级万用表串联在VBUS路径将万用表调至200μA档红表笔接Pico的VBUS焊点黑表笔接电源正极。触发低功耗并观察稳定电流值运行代码用示波器或逻辑分析仪确认WFI执行时刻待电流稳定后读数。常见问题对照表观测电流可能原因排查方向10mA未进入低功耗检查WFI是否执行PM_CSR是否正确写入是否有未清除的中断2–5mA卡在Idle或Sleep检查SLEEP_HIBERNATE位是否为0XOSC是否仍开启100–500μADeep Sleep未生效检查SLEEP_HIBERNATE位是否为1XOSC_CTRL的ENABLE位是否为0ROSC是否运行50μA但唤醒失败唤醒源失效检查WAKE_EN、PADS配置、IO_BANK0_GPIOx_CTRL的IRQOVER我的校准经验同一块Pico在不同环境温度下电流会有±15%波动。夏季实验室30℃测得Deep Sleep电流为28μA冬季10℃则为23μA。因此标称值仅供参考实测才是唯一标准。4. 常见问题与排查技巧实录那些烧掉的电路板教会我的事低功耗开发不是一蹴而就的线性过程而是一场与硬件幽灵的持久战。下面这些问题是我在三年Pico项目中踩过的坑有些甚至让我报废了十几块开发板最终才摸清规律。它们不写在官方手册里但却是量产路上的真实路障。4.1 问题一唤醒后程序跑飞串口无输出现象从Deep Sleep唤醒后LED不亮UART无任何打印示波器显示CLK_SYS信号频率异常如本该12MHz却只有1MHz。根本原因唤醒后未及时切换回XOSC作为系统时钟源导致所有外设包括UART按错误的时钟频率运行波特率计算完全错误。排查步骤用逻辑分析仪抓取CLK_SYS引脚Pico的GPIO21确认唤醒后时钟频率。检查唤醒后代码中是否有*(volatile uint32_t*)0x40028000 0x00000001;切换时钟源。确认XOSC启动等待时间是否足够官方要求≥1ms实测建议≥2ms。解决方案在唤醒后的第一行代码强制执行XOSC启动与切换并加入足够延时。更稳妥的做法是在Reset_Handler中统一处理时钟恢复而非依赖用户代码。4.2 问题二GPIO唤醒偶尔失效概率约30%现象按下唤醒按钮有时能唤醒有时无反应示波器显示按钮信号正常。根本原因PADS_BANK0中GPIO的上拉/下拉电阻配置与唤醒电平不匹配导致引脚在休眠期间处于高阻态易受电磁干扰。排查步骤测量唤醒引脚在休眠期间的电压应稳定在VDD或GND取决于上拉/下拉配置。检查PADS_BANK0_GPIOx寄存器的PULLUP/PULLDOWN位是否与WAKE_LOW/WAKE_HIGH设置一致。用示波器观察引脚在按钮按下瞬间的波形是否存在过长的上升/下降时间1μs。解决方案对于上升沿唤醒必须配置PULLDOWN1且WAKE_HIGH1对于下降沿唤醒配置PULLUP1且WAKE_LOW1。同时在PCB设计时为唤醒引脚添加100nF陶瓷电容滤波可消除90%的误触发。4.3 问题三RTC闹钟唤醒后第二次不再触发现象第一次唤醒正常但之后无论等待多久都不再唤醒。根本原因RTC闹钟是“一次性”的唤醒后RTC_INTR的ALARM位被置1但未被软件清除导致RTC模块认为闹钟已执行不再产生新中断。排查步骤在唤醒后的中断服务函数中添加*(volatile uint32_t*)0x4005c010 0x1;写ALARM位清零。检查是否在清除ALARM位后重新设置了新的RTC_ALARM值。解决方案标准RTC唤醒服务函数模板void rtc_irq_handler() { uint32_t intr *(volatile uint32_t*)0x4005c010; if (intr (1 0)) { // ALARM中断 *(volatile uint32_t*)0x4005c010 0x1; // 清除ALARM标志 // 重新设置下一次闹钟如10秒后 uint32_t next_alarm *(volatile uint32_t*)0x4005c00c 10; // RTC_VALUE 10s *(volatile uint32_t*)0x4005c008 next_alarm; // 执行业务逻辑... } }4.4 问题四Deep Sleep电流高达1.2mA远超标称值现象万用表显示电流1.2mA而预期应为100μA。根本原因未关闭USB PHY供电。即使未连接USB线USBCTRL_DPR寄存器的USB_EN位若为1USB PHY仍消耗约1mA电流。排查步骤检查USBCTRL_DPR0x40050000的USB_EN位是否为0。用万用表测量USB_DP/USB_DM引脚对地电阻若为低阻1kΩ说明PHY仍在工作。解决方案在进入Deep Sleep前强制关闭USB PHY*(volatile uint32_t*)0x40050000 ~(1 0); // USB_EN 0同时确保PADS_BANK0_USB寄存器中的PUPull-up位也被清除避免USB线路漏电。4.5 问题五使用HAL库后低功耗失效现象基于pico-sdk的sleep_goto_sleep()函数调用后电流无明显下降。根本原因pico-sdk的HAL层在进入低功耗前会自动重置部分外设如watchdog、timer但某些重置操作会意外使能中断或修改PM_CSR覆盖用户配置。排查步骤查看pico-sdk源码中sleep_goto_sleep()的实现重点关注其对PM_CSR的操作。在调用sleep_goto_sleep()前后用调试器读取PM_CSR值确认是否被修改。解决方案绕过HAL直接使用裸寄存器操作。或者修改pico-sdk的sleep.c在sleep_goto_sleep()函数末尾添加自定义寄存器写入确保PM_CSR符合预期。我的做法是在main()中完全弃用sleep_goto_sleep()只用__WFI()和手动寄存器配置掌控力更强。5. 从寄存器到产品一个真实环境监测节点的设计闭环理论终要落地。我以一个部署在森林中的温湿度监测节点为例完整展示如何将前述寄存器知识转化为可靠产品。该节点要求每30分钟唤醒一次采集DHT22数据通过LoRa发送然后进入Deep Sleep目标电池寿命≥1年CR2032纽扣电池容量220mAh。5.1 功耗预算与模式选择首先进行严格的功耗预算Deep Sleep电流实测25μA唤醒→采集→发送→休眠全过程耗时约800ms此期间平均电流实测15mA含LoRa发射峰值单次循环总能耗 (25μA × 1799.2s) (15mA × 0.8s) 44.98mC 12mC 56.98mC每日循环次数48次 → 日耗电 56.98mC × 48 2735mC 0.76mAh理论续航 220mAh / 0.76mAh/天 ≈ 289天结论必须采用Deep Sleep模式且所有环节尤其是LoRa发射必须优化到极致。5.2 硬件层关键设计电源管理使用TPS63802 DC-DC转换器将CR2032的2.0–3.0V升压至3.3V效率90%。避免LDO因其静态电流~5μA会吃掉近20%的Deep Sleep预算。唤醒源选用RTC闹钟避免外部按钮的机械故障风险。GPIO唤醒仅作为调试备用。传感器接口DHT22采用单总线唤醒后由GPIO模拟时序无需额外外设节省功耗。LoRa模块SX1276其Standby模式电流仅200nA但TX模式峰值达120mA。因此必须在发送前预充电避免电池电压跌落导致发射失败。5.3 固件层寄存器精控核心代码片段精简版// 初始化RTC设置30分钟闹钟 void rtc_init() { *(volatile uint32_t*)0x4005c000 0x1; // EN1 *(volatile uint32_t*)0x4005c004 0x100000; // DIV≈1Hz *(volatile uint32_t*)0x4005c008 1800; // ALARM1800s (30min) } // Deep Sleep前的终极配置 void enter_deep_sleep() { // 1. 关闭所有无关时钟 *(volatile uint32_t*)0x40028004 0x0; // CLK_PERI_CTRL: disable all *(volatile uint32_t*)0x40028008 0x0; // CLK_ADC_CTRL: disable // 2. 关闭USB PHY *(volatile uint32_t*)0x40050000 ~(1 0); // 3. 配置RTC唤醒 *(volatile uint32_t*)0x4000c010 0x0; // WAKE_EN 0 (禁用GPIO) // 4. 设置PM_CSR uint32_t pm 0; pm | (1 0); // SLEEP_EN pm | (1 1); // SLEEP_HIBERNATE *(volatile uint32_t*)0x4000c000 pm; // 5. 执行WFI __DSB(); __WFI(); } // 唤醒后第一件事恢复XOSC void wake_up_recovery() { *(volatile uint32