本篇总结
使用 PWR,演示停止模式唤醒低功耗例程。
🔑 KEIL MDK、UART调试、PWR
硬件平台:本例程基于 W55MH32L-EVB 开发板运行。
采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB
本示例演示 W55MH32 电源管理外设的使用,管理运行模式,确保系统正常运行的同时尽量降低功耗。本篇深入讲解停止模式(Stop Mode)的进入与唤醒,以及 GPIO 降功耗配置,并通过串口打印状态。
前置工具准备
开始本例程前,请确认以下工具已安装:
- WIZ UartTool V1.0:串口调试工具:点击下载
硬件连接
PWR 为片内外设。串口通过 USART1(PA9/PA10) 打印信息,停止模式唤醒用 PA0(EXTI0)。仅需一根 Micro USB 线连接 DEBUG USB 口。
| 引脚 | 功能 | 说明 |
| PA9 / PA10 | USART1 TX/RX | 串口打印调试信息 |
| PA0 | EXTI0 | 停止模式唤醒引脚(上升沿中断) |
工程结构
工程位于 SDK 内 1.SDK\ModuleDemo\PWR\PWR_Stop\,核心文件如下:
PWR_Stop/
├── USER/
│ ├── main.c ← 停止模式配置 + EXTI0 唤醒 + GPIO 降功耗 + 主循环
│ ├── w55mh32_conf.h ← 外设头文件集中包含
│ ├── w55mh32_it.c/h ← 中断服务函数
│ ├── system_w55mh32.c/h← 系统初始化(时钟配置)
│ └── startup_w55mh32.s ← 启动文件
└── SYSTEM/
└── delay/
└── delay.c/h ← SysTick 延时函数
WFI 与 WFE 指令
进入低功耗模式需调用 WFI(Wait For Interrupt)或 WFE(Wait For Event)内核指令,在 core_cm3.h 中封装为函数:
/* core_cm3.h 中的指令封装 */
#define __WFI __wfi // 等待中断:暂停至任意中断产生
#define __WFE __wfe // 等待事件:暂停至任意事件产生
/* 调用方式 */
__WFI(); // 由中断唤醒
__WFE(); // 由事件唤醒
WFI vs WFE
WFI:由 NVIC 响应的中断唤醒,唤醒后先进入 ISR 再回到主程序;WFE:由事件唤醒(不需 NVIC 使能),唤醒后直接执行下一条指令,延迟更短。WFE 唤醒后不需清除中断挂起位。
停止模式库函数源码解析
PWR_EnterSTOPMode() 封装了进入停止模式的寄存器操作,两个参数控制调压器模式和进入指令。
void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry)
{
uint32_t tmpreg = 0;
/* 设置调压器模式:清除 PDDS/LPDS 位后写入参数 */
tmpreg = PWR->CR;
tmpreg &= CR_DS_MASK;
tmpreg |= PWR_Regulator; // PWR_MainRegulator_ON 或 PWR_Regulator_LowPower
PWR->CR = tmpreg;
/* 置 SLEEPDEEP 位,使 WFI/WFE 进入深度睡眠而非普通睡眠 */
SCB->SCR |= SCB_SCR_SLEEPDEEP;
/* 选择 WFI 或 WFE 进入 */
if (PWR_STOPEntry == PWR_STOPEntry_WFI)
__WFI();
else
__WFE();
/* 唤醒后执行:清除 SLEEPDEEP 位 */
SCB->SCR &= (uint32_t)~((uint32_t)SCB_SCR_SLEEPDEEP);
}
关键点:① PDDS=0 选择停止(非待机);② LPDS 位决定调压器正常或低功耗;③ SLEEPDEEP=1 让 WFI/WFE 进入深度睡眠;④ 唤醒后自动清 SLEEPDEEP,但系统时钟已切换到 HSI(8MHz),需手动重配。
待机模式库函数源码解析
PWR_EnterSTANDBYMode() 置 PDDS=1 + SLEEPDEEP=1,调用 WFI 进入待机。
void PWR_EnterSTANDBYMode(void)
{
/* 清除 Wake-up 标志(必须,否则残留 WUF 阻止进入) */
PWR->CR |= PWR_CR_CWUF;
/* 选择待机模式:PDDS=1 */
PWR->CR |= PWR_CR_PDDS;
/* 置 SLEEPDEEP 位 */
SCB->SCR |= SCB_SCR_SLEEPDEEP;
/* 确保存储操作完毕(Keil 下) */
#if defined ( __CC_ARM )
__force_stores();
#endif
/* 等待中断唤醒——唤醒等同复位 */
__WFI();
}
待机 vs 停止的核心区别
- 待机模式:1.8V 域完全断电,SRAM 和寄存器内容丢失,唤醒后等同芯片复位从头执行。
- 停止模式:保留 SRAM,唤醒后从 WFI 处继续执行。
待机功耗最低,但代价是丢失运行状态。
步骤1:停止模式唤醒例程
串口接收 's' 后,将所有 GPIO 设为模拟输入降功耗,使能 PA0(EXTI0) 上升沿中断唤醒,进入停止模式。PA0 上升沿唤醒后继续执行,调用 SystemInit() 重配时钟并重新初始化串口。
int main(void)
{
RCC_ClocksTypeDef clocks;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
delay_init();
UART_Configuration(115200);
RCC_GetClocksFreq(&clocks);
printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, PCLK2: %3.1fMhz, ADCCLK: %3.1fMhz\n",
(float)clocks.SYSCLK_Frequency / 1000000, (float)clocks.HCLK_Frequency / 1000000,
(float)clocks.PCLK1_Frequency / 1000000, (float)clocks.PCLK2_Frequency / 1000000,
(float)clocks.ADCCLK_Frequency / 1000000);
printf("PWR Stop Test.\n");
printf("Enable WakeUp Pin - PA0\n");
printf("Please Input 's', Come Stop Mode\n");
/* 配置 PA0(EXTI0) 上升沿中断唤醒 */
WAKEUP_Configuration();
/* 等待串口输入 's' */
while (GetCmd() != 's');
/* 所有 GPIO 设为模拟输入,降低功耗 */
GPIO_Configuration();
/* 进入停止模式(低功耗调压器 + WFI) */
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
/* 唤醒后:系统时钟已切到 HSI(8MHz),需重配 */
SystemInit();
UART_Configuration(115200);
printf("WakeUp Success\n");
while (1);
}
void WAKEUP_Configuration(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* EXTI_Line0 = PA0,上升沿触发 */
EXTI_ClearITPendingBit(EXTI_Line0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* PA0: 上拉输入 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
}
void GPIO_Configuration(void)
{
uint8_t i;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_GPIOF | RCC_APB2Periph_GPIOG, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
for (i = 0; i < (GPIOG_BASE - GPIOA_BASE) / 0x400; i++)
{
GPIO_Init((GPIO_TypeDef *)((APB2PERIPH_BASE + (i + 3) * 0x0400)), &GPIO_InitStructure);
}
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All ^ GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) == SET)
{
printf("EXTI->PR = 0x%x\n", EXTI->PR);
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
GPIO_Configuration 降功耗
将 GPIOA-GPIOG 所有引脚设为模拟输入(GPIO_Mode_AIN),可显著降低漏电流。这是低功耗设计的标配操作。
EXTI_PR 挂起寄存器
中断服务函数中打印 EXTI->PR 寄存器值,用于查看挂起状态。唤醒后 PR 位仍为 1,需调用 EXTI_ClearITPendingBit() 清除,否则可能影响下次进入停止模式。
运行验证
打开 WIZ UartTool V1.0,波特率 115200。
停止模式例程:串口打印时钟信息和操作提示后,发送 's' 芯片进入停止模式,串口输出停止;按下 PA0 按键(上升沿)唤醒,芯片从 WFI 处继续执行,重配时钟后打印 "WakeUp Success" 和 EXTI_PR 寄存器值。
SYSCLK: 72.0Mhz, HCLK: 72.0Mhz, PCLK1: 36.0Mhz, PCLK2: 72.0Mhz, ADCCLK: 36.0Mhz
PWR Stop Test.
Enable WakeUp Pin - PA0
Please Input 's', Come Stop Mode
WIZ UartTool 串口输出 — 停止模式进入提示(发送 's' 后停止)
WIZ UartTool 串口输出 — PA0 唤醒后继续执行
(按 PA0 唤醒,从停止处继续执行,重配时钟后打印 WakeUp Success)
常见问题
Q: 进入停止模式后立即被唤醒
PWR_EnterSTOPMode() 前需确保 EXTI_Line0 无挂起标志(调用 EXTI_ClearITPendingBit(EXTI_Line0))。确保进入前 PA0 为低电平,避免上升沿残留触发。
Q: 停止模式唤醒后串口乱码
唤醒后系统时钟自动切到 HSI(8MHz),波特率基于原时钟计算故乱码。唤醒后调用 SystemInit() 重配时钟 + UART_Configuration() 重初始化串口。
Q: 停止模式功耗没降到预期
进入前未关闭 ADC/DAC 等外设导致漏电。设置 ADC_CR2.ADON=0 和 DAC_CR.ENx=0,同时将所有未用 GPIO 设为模拟输入,调压器选 PWR_Regulator_LowPower。
Q: 调试时进入低功耗后断连
低功耗下内核时钟停止导致 SWD/JTAG 断连。设置 DBGMCU_CR 中 DBG_STOP / DBG_STANDBY 位,可在低功耗模式下保持调试时钟。
总结
本文深入讲解了 W55MH32 停止模式的实现与库函数调用:
- WFI/WFE:WFI 由中断唤醒(进 ISR),WFE 由事件唤醒(直接继续,延迟更短),均封装在 core_cm3.h
- 库函数源码:PWR_EnterSTOPMode 设置 PDDS=0+LPDS+SLEEPDEEP,唤醒后继续执行但时钟切 HSI,需调用 SystemInit() 重配
- 降功耗技巧:进入前 GPIO 全设模拟输入、关闭 ADC/DAC、调压器选低功耗模式,可显著降低漏电流
- 唤醒配置:EXTI_Line0(PA0)上升沿中断作为唤醒源,唤醒后清除中断挂起位