全部例程

电源管理-实现低功耗

W55MH32

更新于 2026年9月8日

本篇总结

使用 PWR,演示停止模式唤醒低功耗例程。

🔑 KEIL MDK、UART调试、PWR

硬件平台:本例程基于 W55MH32L-EVB 开发板运行。

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB

本示例演示 W55MH32 电源管理外设的使用,管理运行模式,确保系统正常运行的同时尽量降低功耗。本篇深入讲解停止模式(Stop Mode)的进入与唤醒,以及 GPIO 降功耗配置,并通过串口打印状态。

前置工具准备

开始本例程前,请确认以下工具已安装:

硬件连接

PWR 为片内外设。串口通过 USART1(PA9/PA10) 打印信息,停止模式唤醒用 PA0(EXTI0)。仅需一根 Micro USB 线连接 DEBUG USB 口。

引脚功能说明
PA9 / PA10USART1 TX/RX串口打印调试信息
PA0EXTI0停止模式唤醒引脚(上升沿中断)

工程结构

工程位于 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' 后停止)

(发送 '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)上升沿中断作为唤醒源,唤醒后清除中断挂起位

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5 配套开发板

Andy

W5500 技术支持专家