尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
STM32L431待机模式双路唤醒实战:WKUP引脚与RTC闹钟配置指南
简介这份资源面向使用STM32L431进行低功耗开发的嵌入式工程师与学习者聚焦待机模式下通过Wakeup引脚和RTC闹钟双路唤醒的完整实现方案适合已具备一定HAL库与CubeMX基础、希望掌握电池供电场景节能策略的开发者参考。压缩包共188个文件约1.09MB以C源码与头文件为主体包含28个c、58个h并附带Keil工程文件、CubeMX配置、编译中间产物及可烧录的hex与axf覆盖从外设驱动到工程构建的完整链路。资源围绕待机模式配置、EXTI唤醒引脚中断处理、RTC闹钟定时唤醒及一分钟周期重设等关键环节展开工程中同时启用两种唤醒源便于对比外部事件触发与定时唤醒的差异。目前已有6600人学习下载可作为低功耗项目快速搭建与排错验证的实践参考。1. 用 STM32L431 待机模式把功耗压到微安级wakeup 引脚与 RTC 闹钟双路唤醒怎么落地一块靠纽扣电池撑半年的采集板主控选的是 STM32L431平时绝大多数时间什么都不干只在固定时刻醒来采一次数据或者等外部传感器拉一个电平把它叫醒。这种场景下Stop 模式往往还不够省真正能把电流压到微安级的是待机模式Standby。STM32L431 的待机模式会把内核、大部分外设、SRAM 全部断电只留一小块备份域和唤醒逻辑活着典型电流在 1μA 上下代价是唤醒后相当于一次复位程序从头跑。唤醒源里最常用的两个就是 WKUP 引脚和 RTC 闹钟前者应对突发事件后者应对定时任务。这篇笔记就围绕这两个唤醒路径把进入条件、配置步骤、参数设置和踩过的坑讲清楚适合正在做低功耗节点、又不想被数据手册绕晕的嵌入式工程师。2. 待机模式到底关掉了什么先搞懂 L431 的低功耗分层2.1 从 Run 到 Standby四种模式省电的边界在哪STM32L431 的低功耗模式大致分四档Sleep、Stop 0/1/2、Standby再往下还有 Shutdown。很多人一上来就问「待机模式怎么进」其实更该先问「我到底需要保留什么」。Sleep 只关内核时钟外设照跑电流几百微安到毫安Stop 模式关掉大部分时钟保留 SRAM 和寄存器内容电流能到几微安唤醒后接着往下执行Standby 则把 VCORE 域整个断掉SRAM 内容丢失只有备份域RTC、备份寄存器、RCC 的备份部分靠 VBAT 或 VDD 维持唤醒等同于复位。选型的判断标准很简单唤醒后能不能接受「重新初始化」这件事。如果你的任务是无状态的——醒来读个传感器、发一包数据、继续睡——那 Standby 最划算。如果唤醒后需要接着之前的上下文算比如维持一个协议栈状态机那就得退到 Stop 模式。L431 在 Standby 下典型电流约 1μA具体数值随温度、供电、是否开 RTC 而变而 Stop 2 大概在 1~2μA 量级但保留了 SRAM两者差距没想象中大所以别盲目追最低先看业务能不能承受复位。还有一点容易被忽略进入 Standby 前必须把不用的 GPIO 设成模拟输入或按需下拉浮空的引脚会漏电几微安的账很容易被几个悬空 IO 吃掉。2.2 唤醒源清单为什么 WKUP 和 RTC 是主力L431 能从 Standby 唤醒的源有好几个WKUP 引脚PA0、RTC 闹钟、RTC 唤醒定时器、RTC 入侵检测、外部复位、IWDG 复位等。实际项目里最常用的就是 WKUP 引脚和 RTC 闹钟这一对组合。WKUP 引脚对应 PA0它的特点是可以在 Standby 下工作且支持边沿触发配置。注意 L431 上 PA0 的 WKUP 功能是复用的进入待机前要确保它被正确配置为唤醒源而不是普通 GPIO。RTC 闹钟则依赖备份域只要 VBAT 或 VDD 在RTC 就一直走可以设一个绝对时间点或周期性闹钟。两者可以同时使能谁先到谁先唤醒唤醒后通过读 PWR 的唤醒标志位判断是谁干的——这一点非常关键否则你没法区分「定时到了」还是「外部事件来了」。常见做法是RTC 闹钟负责周期性任务比如每 10 分钟采一次WKUP 引脚接一个外部中断源比如按键或传感器报警两者共用同一个唤醒入口醒来后查标志位分流。2.3 备份域与复位后的世界唤醒不是中断必须建立一个认知Standby 唤醒不是普通中断它是一次系统复位。程序会从复位向量重新开始main之前的所有启动代码都会重跑。区别在于你可以通过读PWR-SR1里的WUF标志、以及 RTC 的标志位判断这次复位是不是从待机唤醒来的以及是谁唤醒的。这意味着你没法在唤醒后「接着睡之前那行代码继续执行」。所有状态要么存进备份寄存器Backup Register掉电不丢要么重新计算。备份寄存器是 L431 上非常实用的资源比如可以把采集计数、上次唤醒时间戳塞进去醒来读出来接着用。另外唤醒后WUF标志需要手动清除否则下次判断会出错。这个标志在PWR-SR1里清除方式是往PWR-SCR写对应位。RTC 的闹钟标志则在RTC-ISR里也要清。忘了清标志是新手最常翻的车之一。3. 进待机前必须做对的配置从时钟到 GPIO 的完整流程3.1 时钟与电源让 RTC 走 LSE别用 LSI 凑合RTC 要长期走时时钟源选择直接影响精度和功耗。L431 的 RTC 可以选 LSE外部 32.768kHz 晶振、LSI内部低速 RC或 HSE 分频。做低功耗产品只要板子上有 32.768kHz 晶振就老老实实用 LSE。LSI 虽然省了晶振但精度差、温漂大一天差几分钟很正常做定时唤醒会越走越偏。配置顺序上先开电源时钟和备份域访问再配置 RTC 时钟源最后初始化 RTC。下面是一段基于 HAL 库的初始化骨架重点看注释里的顺序和参数。/* 使能电源时钟与备份域访问 */ __HAL_RCC_PWR_CLK_ENABLE(); HAL_PWR_EnableBkUpAccess(); /* 允许写备份域 */ /* 选择 LSE 作为 RTC 时钟并等待就绪 */ RCC_OscInitTypeDef osc {0}; osc.OscillatorType RCC_OSCILLATORTYPE_LSE; osc.LSEState RCC_LSE_ON; osc.LSEDriveCapacity RCC_LSEDRIVE_LOW; /* 低驱动能力省电 */ if (HAL_RCC_OscConfig(osc) ! HAL_OK) { Error_Handler(); } /* 把 RTC 时钟源切到 LSE分频后得到 1Hz */ RCC_PeriphCLKInitTypeDef pclk {0}; pclk.PeriphClockSelection RCC_PERIPHCLK_RTC; pclk.RTCClockSelection RCC_RTCCLKSOURCE_LSE; if (HAL_RCCEx_PeriphCLKConfig(pclk) ! HAL_OK) { Error_Handler(); } __HAL_RCC_RTC_ENABLE(); /* 使能 RTC 时钟 */LSEDriveCapacity设成RCC_LSEDRIVE_LOW是为了降低驱动电流前提是晶振负载电容匹配、起振稳定。如果发现 LSE 起振慢或偶尔不起振再往上调驱动档位。RTC 时钟源切换必须在 RTC 未使能或先关再开的情况下做顺序错了会切不过去。3.2 RTC 闹钟配置设一个绝对时间点还是周期RTC 闹钟有两种用法设一个具体的日期时间点Alarm A/B 的完整字段或者只设秒/分字段做周期性触发。做周期采集我一般用「掩码」方式只比较秒和分让闹钟每小时或每十分钟触发一次。RTC_AlarmTypeDef alarm {0}; alarm.AlarmTime.Hours 0; alarm.AlarmTime.Minutes 10; /* 每小时的第 10 分钟触发 */ alarm.AlarmTime.Seconds 0; alarm.AlarmTime.SubSeconds 0; alarm.AlarmTime.DayLightSaving RTC_DAYLIGHTSAVING_NONE; alarm.AlarmTime.StoreOperation RTC_STOREOPERATION_RESET; alarm.AlarmMask RTC_ALARMMASK_DATEWEEKDAY | RTC_ALARMMASK_HOURS; alarm.AlarmSubSecondMask RTC_ALARMSUBSECONDMASK_ALL; alarm.AlarmDateWeekDaySel RTC_ALARMDATEWEEKDAYSEL_DATE; alarm.AlarmDateWeekDay 1; alarm.Alarm RTC_ALARM_A; if (HAL_RTC_SetAlarm_IT(hrtc, alarm, RTC_FORMAT_BIN) ! HAL_OK) { Error_Handler(); }AlarmMask是关键参数。这里把日期和小时都屏蔽掉只比较分钟和秒于是每到「分10、秒0」就触发一次等于每小时一次。如果你想每 10 分钟一次可以把分钟掩码也放开改成在代码里动态改闹钟值。注意闹钟中断在待机下依然有效因为 RTC 属于备份域。设完闹钟后还要使能 RTC 闹钟中断并确保 EXTI 线 17RTC Alarm在 NVIC 里使能。HAL 的HAL_RTC_SetAlarm_IT会处理一部分但 NVIC 使能最好自己确认一遍。3.3 WKUP 引脚配置PA0 的边沿与上拉WKUP 引脚 PA0 要配置成能唤醒待机的模式。HAL 提供了HAL_PWR_EnableWakeUpPin参数里可以指定边沿。/* 使能 WKUP 引脚上升沿唤醒 */ HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1_HIGH); /* PA0 配置为输入带下拉避免浮空漏电 */ GPIO_InitTypeDef gpio {0}; gpio.Pin GPIO_PIN_0; gpio.Mode GPIO_MODE_INPUT; gpio.Pull GPIO_PULLDOWN; HAL_GPIO_Init(GPIOA, gpio);PWR_WAKEUP_PIN1_HIGH表示高电平唤醒对应上升沿。如果你的外部信号是低有效就用PWR_WAKEUP_PIN1_LOW。这里有个细节WKUP 引脚的边沿检测在待机下由 PWR 模块负责和普通 EXTI 不完全一样所以别指望用HAL_GPIO_EXTI那套来配它。下拉是为了防止信号线悬空时引脚漂移导致误唤醒如果外部电路已经有明确电平可以省掉但加上更保险。3.4 进入待机的最后一步清标志、设 SLEEPDEEP、执行 WFI进入待机的代码本身很短但前面的准备工作一样不能少。标准流程是清除所有唤醒标志、设置SLEEPDEEP位、执行WFI。/* 清除之前的唤醒标志避免误判 */ __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); __HAL_RTC_ALARM_CLEAR_FLAG(hrtc, RTC_FLAG_ALRAF); /* 设置 SLEEPDEEP让 WFI 进入深度睡眠 */ HAL_PWR_EnableSleepOnExit(); /* 可选看需求 */ HAL_PWREx_EnableInternalWakeUpLine(); /* 若用内部唤醒线 */ /* 进入待机模式 */ HAL_PWR_EnterSTANDBYMode();HAL_PWR_EnterSTANDBYMode()内部会设SLEEPDEEP并执行WFI。执行到这一行系统就断电了直到某个唤醒源触发。唤醒后程序从复位向量重跑你需要在main开头读标志判断唤醒原因。4. 唤醒之后怎么分辨是谁叫醒的标志位读取与状态恢复4.1 读 PWR 和 RTC 标志区分 WKUP 与闹钟唤醒后第一件事是判断唤醒源。PWR-SR1的WUF位表示发生了唤醒但它不区分是谁。要区分得看 RTC 的闹钟标志和 WKUP 相关状态。/* 判断是否从待机唤醒 */ if (__HAL_PWR_GET_FLAG(PWR_FLAG_WU) ! RESET) { /* 是唤醒事件 */ /* 查 RTC 闹钟标志 */ if (__HAL_RTC_ALARM_GET_FLAG(hrtc, RTC_FLAG_ALRAF) ! RESET) { __HAL_RTC_ALARM_CLEAR_FLAG(hrtc, RTC_FLAG_ALRAF); wakeup_reason WAKEUP_BY_RTC; } else { wakeup_reason WAKEUP_BY_WKUP; } /* 清 PWR 唤醒标志 */ __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); }逻辑很直白先看是不是唤醒事件再看 RTC 闹钟标志在不在。如果闹钟标志没置那基本就是 WKUP 引脚干的。注意 RTC 闹钟标志清除要写RTC-ISR的对应位HAL 宏帮你做了。清完标志再决定后续动作。4.2 用备份寄存器保存跨唤醒状态因为唤醒等于复位普通变量全丢。要保存「采集了多少次」「上次唤醒时间」这类信息用备份寄存器。L431 有若干个 32 位备份寄存器掉电不丢只要 VBAT 或 VDD 在。/* 写备份寄存器 */ HAL_RTCEx_BKUPWrite(hrtc, RTC_BKP_DR0, sample_count); /* 读备份寄存器 */ sample_count HAL_RTCEx_BKUPRead(hrtc, RTC_BKP_DR0); sample_count; HAL_RTCEx_BKUPWrite(hrtc, RTC_BKP_DR0, sample_count);RTC_BKP_DR0是寄存器编号不同型号数量不同L431 一般有十几个可用。写之前要确保备份域访问已使能HAL_PWR_EnableBkUpAccess。这些寄存器在系统复位、待机唤醒后都保留只有备份域掉电才清。用它来存状态机的小状态非常合适比外挂 EEPROM 省事。4.3 唤醒后的初始化裁剪别把时间浪费在重配上既然唤醒是复位所有外设都要重新初始化。但低功耗产品的唤醒时间也是功耗的一部分——醒着越久越费电。所以初始化要裁剪只初始化这次任务需要的外设用完立刻再进待机。比如 RTC 闹钟唤醒通常只需要读传感器、发数据那 UART、SPI 按需开开完就关。GPIO 也要重新配因为复位后都是默认状态。我一般把初始化分成「必须」和「按需」两组唤醒后先跑必须组时钟、电源、RTC 标志判断再根据唤醒原因决定跑哪些按需组。这里有个容易忽略的点如果唤醒后要重新配置 RTC 闹钟记得先清标志再设新闹钟否则可能立刻又触发一次。5. 避坑与排查待机模式最常见的五个翻车现场5.1 现象进了待机电流还是几百微安原因通常不是待机模式没进对而是外围漏电。悬空的 GPIO、没关的外设时钟、外部上拉电阻、稳压器静态电流都会贡献电流。L431 本身待机 1μA但板子上一个没关的 LED 就能吃掉几毫安。解决进待机前把所有未用 GPIO 设成模拟输入GPIO_MODE_ANALOG关掉所有不用的外设时钟检查外部电路有没有常通的上拉或分压。用万用表串在供电上实测别只看手册。5.2 现象RTC 闹钟不触发或者触发时间乱多半是时钟源或掩码配错。用 LSI 会导致走时不准AlarmMask设错会导致闹钟条件永远不满足或频繁触发。还有一种情况是闹钟设在了过去的时间点RTC 不会补触发。解决确认 RTC 时钟源是 LSE 且起振用HAL_RTC_GetTime读当前时间确认闹钟时间在未来检查AlarmMask每一位的含义别把该比较的字段屏蔽了。5.3 现象WKUP 引脚一接上就反复唤醒外部信号抖动、引脚浮空、或者边沿配置和信号极性不匹配都会导致反复唤醒。如果信号本身有毛刺待机下没有软件消抖就会多次触发。解决确认PWR_WAKEUP_PIN1_HIGH/LOW和实际信号极性一致引脚加硬件 RC 滤波或外部上拉/下拉如果信号源本身不稳定考虑改用 RTC 周期唤醒轮询。5.4 现象唤醒后程序跑飞或卡死唤醒是复位如果启动代码、时钟配置、中断向量有问题就会跑飞。常见的是唤醒后立刻又进了待机标志没清或者中断没关导致唤醒瞬间进中断。解决唤醒后先清所有相关标志确认main里的初始化顺序正确检查有没有在待机前使能了不该使能的中断。调试时可以先用 LED 或串口打印唤醒原因确认流程。5.5 现象备份寄存器数据丢失备份寄存器靠备份域供电如果 VBAT 没接或 VDD 掉电时备份域也断了数据就没了。另外写之前没使能备份域访问写不进去。解决确认 VBAT 引脚有供电哪怕接个电容每次写前调HAL_PWR_EnableBkUpAccess读回来验证一下再使用。6. 把双路唤醒做成可复用的低功耗框架前面把单次进入和唤醒讲完了实际产品里更值得做的是把它抽象成一个可复用的框架一个统一的enter_standby()负责收尾一个get_wakeup_reason()负责分流任务逻辑按唤醒原因注册回调。这样换项目时只改业务不动低功耗骨架。具体做法是维护一个唤醒原因枚举和对应的处理函数表。唤醒后读标志查表执行执行完再调enter_standby()。备份寄存器用来存任务计数和上次执行时间避免重复执行。下面是一个简化骨架。typedef enum { WAKEUP_NONE 0, WAKEUP_BY_RTC, WAKEUP_BY_WKUP } wakeup_reason_t; static wakeup_reason_t get_wakeup_reason(void) { if (__HAL_PWR_GET_FLAG(PWR_FLAG_WU) RESET) { return WAKEUP_NONE; /* 上电复位不是唤醒 */ } if (__HAL_RTC_ALARM_GET_FLAG(hrtc, RTC_FLAG_ALRAF) ! RESET) { __HAL_RTC_ALARM_CLEAR_FLAG(hrtc, RTC_FLAG_ALRAF); __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); return WAKEUP_BY_RTC; } __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); return WAKEUP_BY_WKUP; } int main(void) { HAL_Init(); SystemClock_Config(); MX_RTC_Init(); MX_GPIO_Init(); wakeup_reason_t reason get_wakeup_reason(); if (reason WAKEUP_BY_RTC) { do_periodic_task(); /* 定时采集 */ } else if (reason WAKEUP_BY_WKUP) { do_event_task(); /* 外部事件处理 */ } else { do_cold_start(); /* 首次上电 */ } enter_standby(); /* 收尾并再次进入待机 */ while (1) { } }这个骨架的价值在于把「判断唤醒源」和「业务处理」解耦。get_wakeup_reason只负责读标志、清标志、返回枚举业务层不碰寄存器。enter_standby里统一做 GPIO 收尾、清标志、进待机。换唤醒源时只改枚举和判断分支业务回调不动。验证方法上我习惯用两种手段一是用电流表看待机电流是否落到预期区间二是用串口在唤醒后打印原因和备份寄存器值跑几十个周期看有没有错乱。如果条件允许用示波器抓 WKUP 引脚和 RTC 中断脚确认时序。别嫌麻烦低功耗的 bug 往往在跑了几小时甚至几天后才暴露早期验证能省下大量后悔药。最后说个我自己的习惯每次改低功耗相关代码先在enter_standby前加一句把当前状态写进备份寄存器唤醒后读出来对比。这个土办法帮我抓到过好几次「以为进了待机其实没有」的问题。低功耗调试没有捷径就是标志位、电流表、日志三件套反复对。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

Canoe诊断无响应,提示诊断超时,但是CAN报文正常接收发送

Canoe诊断无响应,提示诊断超时,但是CAN报文正常接收发送

打开CANoe, 在Home页面把Simulated Bus改成Real Bus

📅 2026/10/15 15:57:35
除自身以外数组的乘积:前缀积与后缀积的两趟扫描解法

除自身以外数组的乘积:前缀积与后缀积的两趟扫描解法

1. 题目拆解与解题突破口1.1 一道看似简单却容易掉进坑里的数组题LeetCode 238 在算法练习平台上出现频率非常高,题目本身一句话就能说清:给你一个整数数组 nums,返回一个新数组 answer,其中 answer[i] 等于原数组中除 nums[i] 以…

📅 2026/10/15 15:57:35
老版本摄像头驱动在Win10下的安装与DirectShow采集避坑指南

老版本摄像头驱动在Win10下的安装与DirectShow采集避坑指南

简介:大恒DH-HV13、HV20、HV31系列老版本摄像头驱动包,面向仍在使用这些工业相机或老式USB摄像头的工程师、开发者与系统维护人员。该驱动是操作系统与硬件之间的桥梁,解决旧版系统下设备无法被正确识别、图像采集异常或功能受限等问题&#…

📅 2026/10/15 15:57:35
MORE NEWS

更多资讯

📰

STM32+华为云IoT+Qt:从零搭建智能家居全链路控制系统

1. 项目缘起与整体架构拆解智能家居这个词已经被喊了好些年,但真正自己动手从零搭一套完整系统的人其实并不多。大部分爱好者停留在“买个成品模块、下个App、连上WiFi”的阶段,一旦涉及到多端联动、自定义控制逻辑、跨平台界面,就卡住了。我…

📰

Spring Boot动态定时任务:基于数据库配置cron,无需重启实时生效

搞后端的人早晚会碰到这么个需求:定时任务的执行时间得能改,而且不能每次改都重新发版。当时我手上一个项目,运营今天说凌晨跑、明天说挪到中午、后天又要加个针对特定人群的推送任务。如果每改一次就改代码重启,运维不炸我也得炸…

📰

Spring Boot同名类Bean名冲突全解析:从原理到五套解决方案

前两天在排查一个存量项目启动报错时,又看到那条熟悉的异常:Invalid bean definition with name orderConfig。说实话,这类问题在 Spring Boot 项目里太常见了,尤其是多模块开发或者从老项目里批量拷贝代码的时候,两个…

📰

Spring Boot同名类Bean名冲突:原因分析与五套解决方案

我最近在做一个多模块的项目改造,把原来单体应用里的一堆Service拆到不同工程下,结果其中某个微服务在启动时直接给我抛了个红色异常。核心一句话:The bean emailService, defined in class path resource [com/example/notification/EmailSe…

📰

单片机C库运行时:从libspace到中断安全的多任务可重入实践

1. 从 libspace 说起:一个被忽视的运行时角落搞单片机开发的人,平时打交道最多的就是寄存器、外设驱动、RTOS 任务调度这些东西。但有一个东西,它一直在你身边,你却很少正眼看它——C 库运行时。你写memcpy、malloc、sprintf、str…

📰

微信小程序K线图开发实战:HQChart集成与通达信公式解析

简介:这份资源面向微信小程序开发者与股票行情应用爱好者,提供沪深/港股K线图、走势图的完整实现方案,并支持通达信语法解析,适合具备一定前端基础、希望在小程序内搭建专业行情图表的开发者参考。压缩包共72个文件,约…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬