5.16 PWR 使用说明
本章说明 N32G41x 标准外设库中 PWR 驱动的文件组成与 11 个公开函数,内容包括可编程电压检测器(PVD)的阈值与滤波配置、NRST 数字滤波与 IWDG 复位使能,以及 SLEEP、STOP 低功耗模式的进入与主稳压器(MR)低功耗配置;配套的 PVD、SLEEP、STOP 三个示例分别演示电压跨阈值中断、按键唤醒 SLEEP 与唤醒后重建时钟的 STOP。功耗模式、PVD 与 PWR 寄存器的位定义请配合用户手册的“电源控制(PWR)”章节阅读,芯片已知问题以勘误手册为准。
5.16.1 相关文件
| 相对路径 | 作用 |
|---|---|
firmware/n32g41x_std_periph_driver/inc/n32g41x_pwr.h |
定义 PVD 阈值档位、PWR_CTRL2 写保护键、SLEEP、STOP 与 MR 低功耗模式条目宏,并按 PVD 配置、复位配置、低功耗模式、写保护、标志管理五个区段声明 11 个公开函数 |
firmware/n32g41x_std_periph_driver/src/n32g41x_pwr.c |
实现上述 11 个函数:对 PWR_CTRL、PWR_CTRL2 与 SCB_SCR 做读改写,执行 WFI/WFE 进入低功耗模式,并读取 PVD 标志 |
projects/n32g41x_EVAL/examples/PWR/ |
PWR 示例根目录,含 PVD、SLEEP、STOP 3 个示例 |
projects/n32g41x_EVAL/examples/PWR/PVD/ |
该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt |
projects/n32g41x_EVAL/bsp/ |
三个示例共用的板级支持文件:n32g41x_stb.h 中的按键与 LED 定义、毫秒延时与日志输出函数 |
5.16.2 API 参考
以下 11 个公开函数按 n32g41x_pwr.h 的声明顺序分为 5 组;每个条目给出功能摘要与 n32g41x_pwr.c 中该函数正上方的注释原文。
PVD 配置
PWR_PvdEnable、PWR_PVDFilterWidthSet、PWR_PVDLevelConfig
复位配置
PWR_NRSTFilterWidthSet、PWR_EnableIWDGReset
低功耗模式
PWR_EnterSLEEPMode、PWR_EnterSTOPMode、PWR_ConfigMRLPMode、PWR_ConfigMRLPDelay
写保护
PWR_CTRL2WriteProtectionEnable
标志管理
PWR_GetFlagStatus
void PWR_PvdEnable(FunctionalState Cmd)使能或禁用可编程电压检测器(PVD)。
name PWR_PvdEnable.
fun Enables or disables the Power Voltage Detector(PVD).
param Cmd:
- ENABLE
- DISABLE
return none
使用提示: 实现按 Cmd 置位或清除 PWR_CTRL 的 PVDEN 位。
void PWR_PVDFilterWidthSet(uint8_t filter_value)调整 PVD 滤波控制计数值。
name PWR_PVDFilterWidthSet.
fun Adjusting the PVD Filter Control Count value.
param filter_value(Filter Width = (1/fLSI) * filter_value):
-0 ~ 0x1F
return none
使用提示: 实现把 filter_value 写入 PWR_CTRL 位 18:14 的 PVDCNT 字段。
void PWR_PVDLevelConfig(uint32_t level)配置可编程电压检测器(PVD)检测的电压阈值。
name PWR_PVDLevelConfig.
fun Configures the voltage threshold detected by the Power Voltage Detector(PVD).
param PWR_PVDLevel (The input parameters must be the following values):
- PWR_PVD_LEVEL_2V0
- PWR_PVD_LEVEL_2V2
- PWR_PVD_LEVEL_2V4
- PWR_PVD_LEVEL_2V6
- PWR_PVD_LEVEL_2V8
- PWR_PVD_LEVEL_3V0
- PWR_PVD_LEVEL_3V2
- PWR_PVD_LEVEL_3V4
- PWR_PVD_LEVEL_3V6
- PWR_PVD_LEVEL_3V8
- PWR_PVD_LEVEL_4V0
- PWR_PVD_LEVEL_4V2
- PWR_PVD_LEVEL_4V4
- PWR_PVD_LEVEL_4V6
- PWR_PVD_LEVEL_4V8
return none
使用提示: 实现把 level 写入 PWR_CTRL 位 8:5 的 PLS 字段;PWR_PVD_LEVEL_* 宏已按该位序取值。
void PWR_NRSTFilterWidthSet(uint16_t filter_value)调整 NRST 滤波控制计数值。
name PWR_NRSTFilterWidthSet.
fun Adjusting the NRST Filter Control Count value.
param filter_value(Filter Width = (1/fSYSCLK) * filter_value):
-0 ~ 0x7F
return none
使用提示: 实现把 filter_value 写入 PWR_CTRL 位 25:19 的 NRSTCNT 字段。
void PWR_EnableIWDGReset(FunctionalState Cmd)配置 IWDG 计数器到 0 时是否产生复位请求。
name PWR_EnableIWDGReset.
fun Configure whether to generate a reset request when the IWDG counter reaches 0.
param Cmd:
- ENABLE
- DISABLE
return none.
使用提示: 实现按 Cmd 置位或清除 PWR_CTRL 的 IWDGRSTEN 位。
void PWR_EnterSLEEPMode(uint8_t SLEEPONEXIT, uint8_t PWR_SLEEPEntry)进入 SLEEP 模式。
name PWR_EnterSLEEPMode.
fun Enters SLEEP mode.
param SLEEPONEXIT (The input parameters must be the following values):
- PWR_SLEEP_ON_EXIT
- PWR_SLEEP_NOW
param PWR_SLEEPEntry (The input parameters must be the following values):
- PWR_SLEEPENTRY_WFI enter SLEEP mode with WFI instruction
- PWR_SLEEPENTRY_WFE enter SLEEP mode with WFE instruction
return none
使用提示: 实现先清除 SCB_SCR 的 SLEEPDEEP,再按 SLEEPONEXIT 置位或清除 SLEEPONEXIT;传入 PWR_SLEEPENTRY_WFE 时执行 __SEV 与两次 __WFE。
void PWR_EnterSTOPMode(uint8_t enter_mode)进入 STOP 模式。
name PWR_EnterSTOPMode.
fun Enters STOP mode.
param PWR_STOPEntry (The input parameters must be the following values):
- PWR_STOPENTRY_WFI (enter STOP mode with WFI instruction)
- PWR_STOPENTRY_WFE (enter STOP mode with WFE instruction)
return none
使用提示: 实现先置位 SCB_SCR 的 SLEEPDEEP,退出后再清除;传入 PWR_STOPENTRY_WFE 时执行 __SEV 与两次 __WFE。注释记录的形参名为 PWR_STOPEntry,头文件原型中的形参名为 enter_mode。
void PWR_ConfigMRLPMode(uint32_t Mode)配置主稳压器(MR)低功耗模式。
name PWR_ConfigMRLPMode.
fun Configure MR lowpower mode.
param Mode (The input parameters must be the following values):
- PWR_MRLPMODE_FIXED
- PWR_MRLPMODE_AUTO
return none
note This register has write protection. Before writing PWR_CTRL2, PWR_CTRL2WriteProtectionEnable()
function must be called, and then write real value to PWR_CTRL2
使用提示: 实现读改写 PWR_CTRL2 的 MRLPEN 位;该寄存器有写保护,写入前需先调用 PWR_CTRL2WriteProtectionEnable。
void PWR_ConfigMRLPDelay(uint16_t delay_value)调整 MR 低功耗模式的使能延迟时间。
name PWR_ConfigMRLPDelay.
fun Adjusting the MR lowpower mode enable delay time.
param delay_value ((delay time = delay_value*(Tsysclk*2)));
-0 ~ 0xFFF
return none
使用提示: 实现把 delay_value 的低 12 位写入 PWR_CTRL2 位 12:1 的 MRLPDLY 字段。
void PWR_CTRL2WriteProtectionEnable(void)使能 PWR_CTRL2 寄存器写保护。
name PWR_CTRL2WriteProtectionEnable.
fun Enable the PWR_CTRL2 registers write protection.
param none
return none
note This register has write protection. Before writing PWR_CTRL2, PWR_CTRL2WriteProtectionEnable()
function must be called, and then write real value to PWR_CTRL2
使用提示: 实现把 PWR_CTRL2 的 KEY 位字段改写为写保护键 0x57103616。
FlagStatus PWR_GetFlagStatus(uint32_t PWR_FLAG)检查指定的 PWR 标志是否置位。
name PWR_GetFlagStatus.
fun Checks whether the specified PWR flag is set or not.
param PWR_FLAG (The input parameters must be the following values):
- PWR_PVDO_FLAG PVD Output flag
return SET or RESET.
使用提示: 实现读取 PWR_CTRLSTS 与 PWR_FLAG 的按位与结果;参数只登记 PWR_PVDO_FLAG,对应 PWR_CTRLSTS 的 PVDO 位。
5.16.3 Demo 示例说明
本节说明 PWR 的 3 个示例:PVD 用可编程电压检测器监测 VDD、在跨过阈值时中断打印,SLEEP 用 WKUP 按键从睡眠模式唤醒 CPU,STOP 进入停止模式并在唤醒后重建系统时钟。
5.16.3.1 PVD 可编程电压检测中断
工程目录: projects/n32g41x_EVAL/examples/PWR/PVD/
初始化日志串口→配置 EXTI Line16 为上升与下降沿触发→设定 PVD 阈值为 2.4 V 并使能 PVD→等待 VDD 跨过阈值→在 PVD 中断中打印电压高于或低于阈值
| 项目 | 源码解读 |
|---|---|
| 功能 | 用可编程电压检测器(PVD)监测 VDD,电压跨过 2.4 V 阈值时经 EXTI Line16 触发中断,并在串口打印当前电压高于还是低于阈值。 |
| 源码组成 | src/main.c:main、EXTI_Configuration、NVIC_Configuration;inc/main.h:包含 PWR、EXTI、RCC、GPIO 等头文件并声明上述函数;src/n32g41x_it.c:中断服务函数,含 PVD_IRQHandler;inc/n32g41x_it.h:中断服务函数声明;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | PVD 阈值由 PWR_PVD_LEVEL_2V4 设为 2.4 V;PVD 输出接 EXTI Line16,触发沿为上升与下降沿; NVIC 优先级分组为 NVIC_PriorityGroup_1,PVD_IRQn 的抢占与子优先级均为 0。USART1 的 TX 为 PA9,波特率 115200,用于日志输出; 系统时钟为 HSI+PLL,N32G412 为 80 MHz,N32G415 为 96 MHz。 |
| 程序与数据路径 | main 先调用 log_init 打印示例名,再由 EXTI_Configuration 配置 Line16、NVIC_Configuration 使能 PVD_IRQn;PWR_PVDLevelConfig 写入阈值、PWR_PvdEnable 使能 PVD 后进入空主循环。VDD 跨过阈值时 PVD_IRQHandler 清除 EXTI 挂起位,按 PWR_GetFlagStatus(PWR_PVDO_FLAG) 的结果打印高于或低于阈值。 |
| 主要 API | PWR_PVDLevelConfig、PWR_PvdEnable、PWR_GetFlagStatus |
| 验证方法 | 下载并复位后串口打印 This is a PVD demo;调节 VDD 使其跨过 2.4 V,串口分别打印 VDD below threshold! 与 VDD above threshold.。 |
| 使用提示 | 阈值可通过修改 PWR_PVD_LEVEL_2V4 参数调整;支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7,开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM V8.50.1。 |
核心代码解读与移植
示例把 PVD 输出经 EXTI Line16 接到 NVIC,主循环只等待中断发生。
数据路径: PWR_PVDLevelConfig 写入 PWR_CTRL 的 PLS → PWR_PvdEnable 置位 PVDEN → VDD 跨过阈值改变 PVDO → EXTI Line16 触发 PVD_IRQHandler → PWR_GetFlagStatus 读取 PWR_PVDO_FLAG → 串口打印
1. 配置 EXTI Line16 接收 PVD 输出。 PVD 的检测事件内部连到中断线 16,示例先把该线的挂起位清一次,再配成上升与下降沿触发。
EXTI_InitType EXTI_InitStructure;
/* Configure EXTI Line16(PVD Output) to generate an interrupt on rising and
falling edges */
EXTI_ClrITPendBit(EXTI_LINE16);
EXTI_InitStruct(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line = EXTI_LINE16;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitPeripheral(&EXTI_InitStructure);
移植时要同时核对 PVD 所接的 EXTI 线号与触发沿,以及该线对应的中断服务函数。
2. 使能 PVD 中断通道。 PVD_IRQn 的抢占与子优先级都取 0,优先级分组选用一位抢占。
NVIC_InitType NVIC_InitStructure;
/* Configure one bit for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* Enable the PVD Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = PVD_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
改动优先级时要同步改这里的抢占与子优先级,并核对与其它中断的相对次序。
3. 设定阈值并打开 PVD。 阈值先由 PWR_PVDLevelConfig 写入 PWR_CTRL 的 PLS 字段,再由 PWR_PvdEnable 置位 PVDEN;此后主循环为空。
/* Configure the PVD Level to 2.4V ,from 2V ~ 4.8V,one step mean add 200mv*/
PWR_PVDLevelConfig(PWR_PVD_LEVEL_2V4);
/* Enable the PVD Output */
PWR_PvdEnable(ENABLE);
while (1)
{
}
移植时按需要的报警电压改 PWR_PVD_LEVEL_* 宏,从 2.0 V 到 4.8 V 每档相差 200 mV。
4. 中断中读标志并打印。 进入中断后先清 EXTI Line16 的挂起位,再用 PWR_GetFlagStatus 判断 PVDO,据此打印电压方向。
if (EXTI_GetStatusFlag(EXTI_LINE16) != RESET)
{
/* Clear the EXTI line 16 pending bit */
EXTI_ClrITPendBit(EXTI_LINE16);
/* Check PVD output */
if (PWR_GetFlagStatus(PWR_PVDO_FLAG) != RESET)
{
/* VDD is below PVD threshold */
printf("PVD IRQ: VDD below threshold!\r\n");
}
else
{
/* VDD is above PVD threshold */
printf("PVD IRQ: VDD above threshold.\r\n");
}
}
改写判断方向或输出内容时,保持先清挂起位再读标志的顺序,避免下次中断被漏掉。
对应源码: src/main.c、src/n32g41x_it.c
5.16.3.2 SLEEP 睡眠模式按键唤醒
工程目录: projects/n32g41x_EVAL/examples/PWR/SLEEP/
初始化日志串口→配置 WKUP 按键为上升沿中断输入→延时 1 s 后进入 SLEEP 模式→WKUP 按键中断唤醒 CPU→打印进入与退出 SLEEP 的信息
| 项目 | 源码解读 |
|---|---|
| 功能 | 用 WKUP 按键(PA0)产生外部中断把 CPU 从 SLEEP 模式唤醒; CPU 停止执行,唤醒后无需重新配置时钟即恢复运行。 |
| 源码组成 | src/main.c:main、KeyInputExtiInit;inc/main.h:包含 PWR、EXTI、RCC、GPIO 等头文件,定义 WKUP 按键的端口、引脚与 EXTI 线宏;src/n32g41x_it.c:中断服务函数,含 KEY_EXTI_IRQHandler;inc/n32g41x_it.h:中断服务函数声明;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | WKUP 按键为 PA0,配为 GPIO_MODE_INPUT 输入,映射到 EXTI_LINE0,触发沿为上升沿;NVIC 优先级分组为 NVIC_PriorityGroup_1,EXTI0_IRQn 的抢占与子优先级均为 0。USART1 的 TX 为 PA9,波特率 115200; 系统时钟为 HSI+PLL,N32G412 为 80 MHz,N32G415 为 96 MHz。 |
| 程序与数据路径 | main 调用 log_init 后由 KeyInputExtiInit 把 PA0 配为输入、接 EXTI0 并使能 EXTI0_IRQn,清一次挂起标志后进入循环:先 SysTick_Delay_Ms(1000) 延时并打印进入信息,再用 PWR_EnterSLEEPMode(PWR_SLEEP_NOW, PWR_SLEEPENTRY_WFI) 进入 SLEEP,按键中断唤醒后打印退出信息。 |
| 主要 API | PWR_EnterSLEEPMode |
| 验证方法 | 下载并复位后串口打印 --- PWR_SLEEP Demo ---,随后每秒打印 Entry SLEEP mode 并进入睡眠;按下 WKUP 按键唤醒后串口打印 Exit SLEEP mode。 |
| 使用提示 | 评估功耗时应去掉打印; 支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7,开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM V8.50.1。 |
核心代码解读与移植
示例先配置 WKUP 按键的 EXTI0 中断,主循环延时后进入 SLEEP,由按键唤醒并继续执行。
数据路径: KeyInputExtiInit 把 PA0 配为输入并接 EXTI0 → PWR_EnterSLEEPMode(PWR_SLEEP_NOW, PWR_SLEEPENTRY_WFI) 清 SLEEPDEEP 后执行 WFI → 按键按下触发 EXTI0_IRQHandler 清除挂起位 → CPU 继续运行并打印退出信息
1. 用宏给出 WKUP 按键与 EXTI 线。 inc/main.h 把按键端口、引脚源、EXTI 线、中断向量与中断服务函数名集中定义,后续配置都通过这组宏访问。
/* WKUP EXTI wakeup configuration (PA0 -> EXTI0) */
#define KEY_EXTI_PORT_SOURCE GPIOA_PORT_SOURCE
#define KEY_EXTI_PIN_SOURCE GPIO_PIN_SOURCE0
#define KEY_EXTI_LINE EXTI_LINE0
#define KEY_EXTI_IRQn EXTI0_IRQn
#define KEY_EXTI_IRQHandler EXTI0_IRQHandler
换用其它按键时,同步改这组宏的端口、引脚源与 EXTI 线,并保持中断服务函数名与向量一致。
2. 把按键引脚配成输入并接 EXTI0。 KeyInputExtiInit 打开按键所在端口的时钟,把引脚配为输入,再用 GPIO_ConfigEXTILine 选择端口源与引脚源,最后配置 EXTI 线上升沿触发并使能。
/* Enable the GPIO Clock */
BUTTON_WKUP_CLK_ENABLE;
/*Configure the GPIO pin as input floating*/
GPIO_InitStruct(&GPIO_InitStructure);
GPIO_InitStructure.Pin = Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_INPUT;
GPIO_InitPeripheral(GPIOx, &GPIO_InitStructure);
/*Configure key EXTI Line to key input Pin*/
GPIO_ConfigEXTILine(KEY_EXTI_PORT_SOURCE, KEY_EXTI_PIN_SOURCE);
/*Configure key EXTI line*/
EXTI_InitStruct(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line = KEY_EXTI_LINE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitPeripheral(&EXTI_InitStructure);
移植时按实际按键的端口与引脚改 BUTTON_WKUP_PORT、BUTTON_WKUP_PIN 与 GPIO_ConfigEXTILine 的端口源、引脚源。
3. 延时后进入 SLEEP 并打印进出信息。 主循环每次先延时 1 s,打印进入信息后调用 PWR_EnterSLEEPMode 进入 SLEEP,被唤醒后打印退出信息。
while (1)
{
/* Insert a long delay */
SysTick_Delay_Ms(1000);
log_info("Entry SLEEP mode\n");
/* Request to enter SLEEP mode*/
PWR_EnterSLEEPMode(PWR_SLEEP_NOW, PWR_SLEEPENTRY_WFI);
log_info("Exit SLEEP mode\n");
}
第一个实参取 PWR_SLEEP_NOW 表示立即休眠,改延时参数可调整两次休眠之间的间隔。
4. 中断服务清除 EXTI 挂起位。 按键中断进入后判断该线的状态标志,若置位则清挂起位,使 CPU 退出 SLEEP 并返回主循环。
void KEY_EXTI_IRQHandler(void)
{
if (EXTI_GetStatusFlag(KEY_EXTI_LINE) != RESET)
{
EXTI_ClrStatusFlag(KEY_EXTI_LINE);
}
}
移植时要核对中断服务函数名与启动文件中 EXTI0_IRQHandler 的映射,以及清除挂起位所用标志与 EXTI 线一致。
对应源码: src/main.c、src/n32g41x_it.c、inc/main.h
5.16.3.3 STOP 停止模式按键唤醒
工程目录: projects/n32g41x_EVAL/examples/PWR/STOP/
初始化日志串口→配置 WKUP 按键为上升沿中断输入→延时 1 s 后进入 STOP 模式→WKUP 按键中断唤醒 MCU→重新配置系统时钟并打印退出信息
| 项目 | 源码解读 |
|---|---|
| 功能 | 用 WKUP 按键(PA0)的外部中断把 MCU 从 STOP 模式唤醒; 唤醒后重新配置系统时钟再恢复运行。 |
| 源码组成 | src/main.c:main、KeyInputExtiInit、SetSysClockToPLL;inc/main.h:包含 PWR、EXTI、RCC、GPIO、FLASH 等头文件,定义 WKUP 按键的端口、引脚与 EXTI 线宏;src/n32g41x_it.c:中断服务函数,含 KEY_EXTI_IRQHandler;inc/n32g41x_it.h:中断服务函数声明;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | WKUP 按键为 PA0,配为 GPIO_MODE_INPUT 输入,映射到 EXTI_LINE0,触发沿为上升沿;NVIC 优先级分组为 NVIC_PriorityGroup_1,EXTI0_IRQn 的抢占与子优先级均为 0。唤醒后 SetSysClockToPLL(RCC_PLL_SRC_HSI,80000000) 以 HSI 为 PLL 源重建 80 MHz 系统时钟;USART1 的 TX 为 PA9,波特率 115200。 |
| 程序与数据路径 | main 调用 log_init 后由 KeyInputExtiInit 把 PA0 接 EXTI0 并使能 EXTI0_IRQn,进入循环:SysTick_Delay_Ms(1000) 延时并打印进入信息,再用 PWR_EnterSTOPMode(PWR_STOPENTRY_WFI) 进入 STOP;按键唤醒后 SetSysClockToPLL(RCC_PLL_SRC_HSI,80000000) 重建时钟,延时 10 ms、重新 log_init 并打印退出信息。 |
| 主要 API | PWR_EnterSTOPMode |
| 验证方法 | 下载并复位后串口打印 --- Reset for STOP ---,随后每秒打印 Entry STOP mode 并进入 STOP;按下 WKUP 按键唤醒后串口打印 Exit STOP mode。 |
| 使用提示 | 评估功耗时应去掉打印; 唤醒后必须重新配置时钟与串口,readme 说明唤醒后可观察到 LED1 恢复闪烁; 支持 N32G412x8L7、N32G412xBL7、N32G415x8L7、N32G415xBL7,开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM V8.50.1。 |
核心代码解读与移植
示例先配置 WKUP 按键中断,主循环进入 STOP,唤醒后重建系统时钟再继续运行。
数据路径: KeyInputExtiInit 把 PA0 接 EXTI0 → PWR_EnterSTOPMode(PWR_STOPENTRY_WFI) 置位 SLEEPDEEP 后执行 WFI → 按键唤醒 → SetSysClockToPLL 重新使能 HSI 与 PLL → 打印退出信息
1. 把 WKUP 按键接到 EXTI0 上升沿。 KeyInputExtiInit 用 GPIO_ConfigEXTILine 选择端口源与引脚源,再配置 EXTI0 为中断模式、上升沿触发并使能。
/*Configure key EXTI Line to key input Pin*/
GPIO_ConfigEXTILine(KEY_EXTI_PORT_SOURCE, KEY_EXTI_PIN_SOURCE);
/*Configure key EXTI line*/
EXTI_InitStruct(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line = KEY_EXTI_LINE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitPeripheral(&EXTI_InitStructure);
移植时按实际唤醒引脚改端口源与引脚源宏,并核对该 EXTI 线在 STOP 模式下具备唤醒能力。
2. 主循环进入 STOP 并在唤醒后重建时钟。 主循环延时 1 s 后打印进入信息并调用 PWR_EnterSTOPMode,被按键唤醒后调用 SetSysClockToPLL 恢复时钟,再延时 10 ms 并重新初始化日志。
while (1)
{
/* Insert a long delay */
SysTick_Delay_Ms(1000);
log_info("Entry STOP mode\n");
/* Request to enter STOP mode*/
PWR_EnterSTOPMode(PWR_STOPENTRY_WFI);
/* Configures system clock after wake-up from STOP: enable HSI, PLL and select
PLL as system clock source */
SetSysClockToPLL(RCC_PLL_SRC_HSI,80000000);
SysTick_Delay_Ms(10);
log_init();
log_info("\nExit STOP mode\n");
}
从 STOP 唤醒到时钟重建之间串口不可用,因此退出信息排在 SetSysClockToPLL 与重新 log_init 之后。
3. 时钟重建函数按目标频率选择 PLL 参数。 SetSysClockToPLL 以 80 MHz 为目标选择倍频系数、输出分频与 FLASH 等待周期,并设置各总线分频后把 PLL 切为系统时钟。
case 80000000:
/* 4MHz * 40 = 160MHz(FVCO) / 2(PLLOD) /1 = 80MHz(Fpll) */
latency = FLASH_LATENCY_2; /* 72MHz < SYSCLK <= 80MHz */
pllmul = 40;
plloutdiv = RCC_PLL_OD_DIV_2;
pllsysdiv = DISABLE;
break;
default:
return ERROR;
}
/* HCLK = SYSCLK */
RCC_ConfigHclk(RCC_SYSCLK_DIV1);
/* PCLK2 = HCLK */
RCC_ConfigPclk2(RCC_HCLK_DIV1);
/* PCLK1 = HCLK/2 */
RCC_ConfigPclk1(RCC_HCLK_DIV2);
移植时按实际晶振与目标主频改 pllmul、plloutdiv、pllsysdiv,并同步调整 FLASH_LATENCY 与各总线分频。
对应源码: src/main.c
5.16.4 注意与排障
| 场景 | 检查与建议 | 来源 |
|---|---|---|
| STOP 模式唤醒后的系统时钟 | 从 STOP 模式经中断或唤醒事件退出时,系统时钟默认切为 HSI,待时钟源就绪后硬件再自动恢复到进入 STOP 之前的状态; 进入前以 PLL 为系统时钟的,需等待 HSI 或 HSE 就绪后由硬件使能并切回 PLL。 |
用户手册 |
| STOP 模式的进入被延迟 | STOP 模式下若正在执行 FLASH 操作或正在访问 APB 区域,进入 STOP 的时间会分别延迟到内存访问或 APB 访问完成。 | 用户手册 |
| STOP 模式下可用的唤醒源 | STOP 模式下 IWDG、RTC/BTIM1/PVD/COMP 与 USART1/4 可配置为唤醒源,PLL、HSI、HSE 时钟关闭,LSE/LSI 可配置运行,SRAM、寄存器与 GPIO 状态保持。 | 用户手册 |
| 写 PWR_CTRL2 前需先开写保护 | PWR_CTRL2 有写保护,修改前需先向该寄存器写入 32 位键 0x57103616,再执行写操作;主稳压器低功耗模式与延迟时间都由该寄存器控制。 |
用户手册 |
| PVD 阈值与中断触发 | PVD 由 PWR_CTRL.PLS[3:0] 选择 2.0 V~4.8 V 的报警阈值(每档相差 200 mV),由 PWR_CTRL.PVDEN 使能;PWR_CTRLSTS.PVDO 指示 VDD/VDDA 是否高于阈值,该事件内部连接到 EXTI 中断线 16,可配置上升或下降沿触发中断。 |
用户手册 |
| 滤波器旁路 | PWR_CTRL.PVDCNT 与 NRSTCNT 全为 0 时对应滤波器旁路;NRST 滤波在 STOP 模式下旁路。 |
用户手册 |
N32G412、N32G415 系列勘误手册 V1.0.0(2026.6.30)的勘误列表只列出一行 A 版芯片条目,勘误链接列为空,未列出 PWR 相关条目。