第二十七章 电源管理——实现低功耗

单芯片解决方案,开启全新体验——W55MH32 高性能以太网单片机

W55MH32是WIZnet重磅推出的高性能以太网单片机,它为用户带来前所未有的集成化体验。这颗芯片将强大的组件集于一身,具体来说,一颗W55MH32内置高性能Arm® Cortex-M3核心,其主频最高可达216MHz;配备1024KB FLASH与96KB SRAM,满足存储与数据处理需求;集成TOE引擎,包含WIZnet全硬件TCP/IP协议栈、内置MAC以及PHY,拥有独立的32KB以太网收发缓存,可供8个独立硬件socket使用。如此配置,真正实现了All-in-One解决方案,为开发者提供极大便利。

在封装规格上,W55MH32 提供了两种选择:QFN100和QFN68。

W55MH32L采用QFN100封装版本,尺寸为12x12mm,其资源丰富,专为各种复杂工控场景设计。它拥有66个GPIO、3个ADC、12通道DMA、17个定时器、2个I2C、5个串口、2个SPI接口(其中1个带I2S接口复用)、1个CAN、1个USB2.0以及1个SDIO接口。如此丰富的外设资源,能够轻松应对工业控制中多样化的连接需求,无论是与各类传感器、执行器的通信,还是对复杂工业协议的支持,都能游刃有余,成为复杂工控领域的理想选择。 同系列还有QFN68封装的W55MH32Q版本,该版本体积更小,仅为8x8mm,成本低,适合集成度高的网关模组等场景,软件使用方法一致。更多信息和资料请进入网站或者私信获取。

此外,本W55MH32支持硬件加密算法单元,WIZnet还推出TOE+SSL应用,涵盖TCP SSL、HTTP SSL以及 MQTT SSL等,为网络通信安全再添保障。

为助力开发者快速上手与深入开发,基于W55MH32L这颗芯片,WIZnet精心打造了配套开发板。开发板集成WIZ-Link芯片,借助一根USB C口数据线,就能轻松实现调试、下载以及串口打印日志等功能。开发板将所有外设全部引出,拓展功能也大幅提升,便于开发者全面评估芯片性能。

若您想获取芯片和开发板的更多详细信息,包括产品特性、技术参数以及价格等,欢迎访问官方网页,我们期待与您共同探索W55MH32的无限可能。

wKgZPGgbOfaANhwzACodXd3sVzg463.png

第二十七章 电源管理——实现低功耗

本章参考资料:《W55MH32参考手册》。

1 W55MH32的电源管理简介

电源对电子设备的重要性不言而喻,它是保证系统稳定运行的基础,而保证系统能稳定运行后,又有低功耗的要求。 在很多应用场合中都对电子设备的功耗要求非常苛刻,如某些传感器信息采集设备,仅靠小型的电池提供电源,要求工作长达数年之久, 且期间不需要任何维护;由于智慧穿戴设备的小型化要求,电池体积不能太大导致容量也比较小,所以也很有必要从控制功耗入手, 提高设备的续行时间。因此,W55MH32有专门的电源管理外设监控电源并管理设备的运行模式,确保系统正常运行,并尽量降低器件的功耗。

1.1 电源监控

W55MH32芯片主要通过引脚VDD从外部获取电源,在它的内部具有电源监控器用于检测VDD的电压, 以实现复位功能及掉电紧急处理功能,保证系统可靠地运行。

1.1.1 上电复位与掉电复位(POR与PDR)

当检测到VDD的电压低于阈值VPOR及VPDR时,无需外部电路辅助,W55MH32芯片会自动保持在复位状态,防止因电压不足强行工作而带来严重的后果。 见下图,POR与PDR 。在刚开始电压低于VPOR时(约1.92V), W55MH32保持在上电复位状态(POR,Power On Reset),当VDD电压持续上升至大于VPOR时,芯片开始正常运行,而在芯片正常运行的时候, 当检测到VDD电压下降至低于VPDR阈值(约1.88V),会进入掉电复位状态(PDR,Power Down Reset)。

1.1.2 可编程电压检测器PVD

上述POR、PDR功能是使用其电压阈值与外部供电电压VDD比较,当低于工作阈值时,会直接进入复位状态,这可防止电压不足导致的误操作。 除此之外,W55MH32还提供了可编程电压检测器PVD,它也是实时检测VDD的电压,当检测到电压低于编程的VPVD阈值时, 会向内核产生一个PVD中断(EXTI16线中断)以使内核在复位前进行紧急处理。该电压阈值可通过电源控制寄存器PWR_CSR设置。

使用PVD可配置8个等级,见下表,PVD的阈值等级 。 其中的上升沿和下降沿分别表示类似图 POR与PDR 中VDD电压上升过程及下降过程的阈值。

阈值等级 条件 最小值 典型值 最大值 单位
级别 0 上升沿 2.1 2.18 2.26 V
级别 0 下降沿 2 2.08 2.16 V
级别 1 上升沿 2.19 2.28 2.37 V
级别 1 下降沿 2.09 2.18 2.27 V
级别 2 上升沿 2.28 2.38 2.48 V
级别 2 下降沿 2.18 2.28 2.38 V
级别 3 上升沿 2.38 2.48 2.58 V
级别 3 下降沿 2.28 2.38 2.48 V
级别 4 上升沿 2.47 2.58 2.69 V
级别 4 下降沿 2.37 2.48 2.59 V
级别 5 上升沿 2.57 2.68 2.79 V
级别 5 下降沿 2.47 2.58 2.69 V
级别 6 上升沿 2.66 2.78 2.9 V
级别 6 下降沿 2.56 2.68 2.8 V
级别 7 上升沿 2.76 2.88 3 V
级别 7 下降沿 2.66 2.78 2.9 V

1.2 W55MH32的电源系统

为了方便进行电源管理,W55MH32把它的外设、内核等模块根据功能划分了供电区域, 其内部电源区域划分见下图,W55MH32的电源系统 :

wKgZO2gxeqmAHrp9AAGfAIUISRU225.png

从框图了解到,W55MH32的电源系统主要分为备份域电路、内核电路以及ADC电路三部分,介绍如下:

ADC电源及参考电压(VDDA供电区域)

为了提高转换精度,W55MH32的ADC配有独立的电源接口,方便进行单独的滤波。 ADC的工作电源使用VDDA引脚输入,使用VSSA作为独立的地连接, VREF引脚则为ADC提供测量使用的参考电压。

调压器供电电路(VDD/1.8V供电区域)

在W55,H32的电源系统中调压器供电的电路是最主要的部分,调压器为备份域及待机电路以外的所有数字电路供电,其中包括内核、 数字外设以及RAM,调压器的输出电压约为1.8V,因而使用调压器供电的这些电路区域被称为1.8V域。

调压器可以运行在“运行模式”、“停止模式”以及“待机模式”。在运行模式下,1.8V域全功率运行;在停止模式下1.8V域运行在低功耗状态, 1.8V区域的所有时钟都被关闭,相应的外设都停止了工作,但它会保留内核寄存器以及SRAM的内容;在待机模式下,整个1.8V域都断电, 该区域的内核寄存器及SRAM内容都会丢失(备份区域的寄存器不受影响)。

备份域电路(后备供电区域)

W55MH32的LSE振荡器、RTC及备份寄存器这些器件被包含进备份域电路中,这 部分的电路可以通过W55MH32的VBAT引脚获取供电电源, 在实际应用中一般会使用3V的纽扣电池对该引脚供电。

在图中备份域电路的左侧有一个电源开关结构,它的功能类似图 双二极管结构 中的双二极管, 在它的“1”处连接了VBAT电源,“2”处连接了VDD主电源(一般为3.3V), 右侧“3”处引出到备份域电路中。当VDD主电源存在时,由于VDD电压较高, 备份域电路通过VDD供电,节省纽扣电池的电源,仅当VDD掉电时, 备份域电路由纽扣电池通过VBAT供电,保证电路能持续运行,从而可利用它保留关键数据。

1.3 W55MH32的功耗模式

按功耗由高到低排列,W55MH32具有运行、睡眠、停止和待机四种工作模式。上电复位后W55MH32处于运行状态时,当内核不需要继续运行, 就可以选择进入后面的三种低功耗模式降低功耗,这三种模式中,电源消耗不同、唤醒时间不同、唤醒源不同,用户需要根据应用需求, 选择最佳的低功耗模式。三种低功耗的模式说明见下表,W55MH32的低功耗模式说明:

模式 说明 进入方式 唤醒方式 对 1.8V 区域时钟的影响 对 VDD 区域时钟的影响 调压器
睡眠 内核停止,所有外设(如 NVIC、SysTick 等)仍运行 调用 WFI 命令

调用 WFE 命令

任一中断

唤醒事件

内核时钟关,其他时钟和 ADC 时钟无影响
停止 所有时钟停止 配置PWR_CR 寄存器的PDDS + LPDS + SLEEPDEEP 位 + WFI/WFE 命令 任一外部中断(在外部中断寄存器中设置) 关闭所有 1.8V 区域的时钟 HSI 和 HSE 振荡器关闭 开启或低功耗模式(依电源控制寄存器设定)
待机 1.8V 电源关闭 配置PWR_CR 寄存器的PDDS + SLEEPDEEP 位 + WFI/WFE 命令 WKUP 引脚上升沿、RTC 闹钟事件、NRST 外部复位、IWDG 复位 - -

从表中可以看到,这三种低功耗模式层层递进,运行的时钟或芯片功能越来越少,因而功耗越来越低。

1.3.1 睡眠模式

在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,CM3核心的外设全都还照常运行。有两种方式进入睡眠模式, 它的进入方式决定了从睡眠唤醒的方式,分别是WFI(wait for interrupt)和WFE(wait for event),即由等待“中断”唤醒和由“事件”唤醒。 睡眠模式的各种特性见下表,睡眠模式的各种特性:

特性 说明
立即睡眠 在执行 WFI 或 WFE 指令时立即进入睡眠模式。
退出时睡眠 在退出优先级最低的中断服务程序后才进入睡眠模式。
进入方式 内核寄存器的SLEEPDEEP = 0,然后调用 WFI 或 WFE 指令即可进入睡眠模式;

另外若内核寄存器的SLEEPONEXIT=0 时,进入 “立即睡眠” 模式,SLEEPONEXIT=1 时,进入 “退出时睡眠” 模式。

唤醒方式 如果是使用 WFI 指令睡眠的,则可使用任意中断唤醒;

如果是使用 WFE 指令睡眠的,则由事件唤醒。

睡眠时 关闭内核时钟,内核停止,而外设正常运行,在软件上表现为不再执行新的代码。这个状态会保留睡眠前的内核寄存器、内存的数据。
唤醒延迟 无延迟。
唤醒后 若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。

1.3.2 停止模式

在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其1.8V区域的部分电源没有关闭, 还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。 停止模式可以由任意一个外部中断(EXTI)唤醒,在停止模式中可以选择电压调节器为开模式或低功耗模式。 停止模式的各种特性见下表,停止模式的各种特性:

特性 说明
调压器低功耗模式 在停止模式下调压器可工作在正常模式或低功耗模式,可进一步降低功耗
进入方式 内核寄存器的SLEEPDEEP =1,PWR_CR 寄存器中的PDDS=0,然后调用 WFI 或 WFE 指令即可进入停止模式;

PWR_CR 寄存器的LPDS=0 时,调压器工作在正常模式,LPDS=1 时工作在低功耗模式;

唤醒方式 如果是使用 WFI 指令睡眠的,可使用任意 EXTI 线的中断唤醒;

如果是使用 WFE 指令睡眠的,可使用任意配置为事件模式的 EXTI 线事件唤醒。

停止时 内核停止,片上外设也停止。这个状态会保留停止前的内核寄存器、内存的数据。
唤醒延迟 基础延迟为 HSI 振荡器的启动时间,若调压器工作在低功耗模式,还需要加上调压器从低功耗切换至正常模式下的时间。
唤醒后 若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。唤醒后,会使用 HSI 作为系统时钟。

1.3.3 待机模式

待机模式,它除了关闭所有的时钟,还把1.8V区域的电源也完全关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录, 只能对芯片复位,重新检测boot条件,从头开始执行程序。它有四种唤醒方式,分别是WKUP(PA0)引脚的上升沿,RTC闹钟事件, NRST引脚的复位和IWDG(独立看门狗)复位。

特性 说明
进入方式 内核寄存器的SLEEPDEEP =1,PWR_CR 寄存器中的PDDS=1,PWR_CR 寄存器中的唤醒状态位WUF=0,然后调用 WFI 或 WFE 指令即可进入待机模式;
唤醒方式 通过 WKUP 引脚的上升沿,RTC 闹钟、唤醒、入侵、时间戳事件或 NRST 引脚外部复位及 IWDG 复位唤醒。
待机时 内核停止,片上外设也停止;内核寄存器、内存的数据会丢失;除复位引脚、RTC_AF1 引脚及 WKUP 引脚,其它 I/O 口均工作在高阻态。
唤醒延迟 芯片复位的时间
唤醒后 相当于芯片复位,在程序表现为从头开始执行代码。

在以上讲解的睡眠模式、停止模式及待机模式中,若备份域电源正常供电, 备份域内的RTC都可以正常运行,备份域内的寄存器的数据会被保存,不受功耗模式影响。

2 电源管理相关的库函数及命令

W55MH32标准库对电源管理提供了完善的函数及命令,使用它们可以方便地进行控制,本小节对这些内容进行讲解。

2.1 配置PVD监控功能

PVD可监控VDD的电压,当它低于阈值时可产生PVD中断以让系统进行紧急处理, 这个阈值可以直接使用库函数PWR_PVDLevelConfig配置成前面表 PVD的阈值等级 中说明的阈值等级。

2.2 WFI与WFE命令

我们了解到进入各种低功耗模式时都需要调用WFI或WFE命令,它们实质上都是内核指令, 在库文件core_cm3.h中把这些指令封装成了函数,见代码清单:电源管理-1 :

代码清单:电源管理-1 WFI与WFE的指令定义(core_cm3.h文件)

/** brief  等待中断

    等待中断 是一个暂停执行指令
    暂停至任意中断产生后被唤醒
*/
#define __WFI                             __wfi


/** brief  等待事件

    等待事件 是一个暂停执行指令
    暂停至任意事件产生后被唤醒
*/
#define __WFE                             __wfe

对于这两个指令,我们应用时一般只需要知道,调用它们都能进入低功耗模式, 需要使用函数的格式“__WFI();”和“__WFE();”来调用(因为__wfi及__wfe是编译器内置的函数,函数内部调用了相应的汇编指令)。 其中WFI指令决定了它需要用中断唤醒,而WFE则决定了它可用事件来唤醒,关于它们更详细的区别可查阅《cortex-CM3/CM4权威指南》了解。

2.3 进入停止模式

直接调用WFI和WFE指令可以进入睡眠模式,而进入停止模式则还需要在调用指令前设置一些寄存器位, W55MH32标准库把这部分的操作封装到PWR_EnterSTOPMode函数中了,它的定义见代码清单:电源管理-2 :

代码清单:电源管理-2 进入停止模式

/**
* @brief 进入停止模式
*
* @note   在停止模式下所有I/O的会保持在停止前的状态
* @note   从停止模式唤醒后,会使用HSI作为时钟源
* @note   调压器若工作在低功耗模式,可减少功耗,但唤醒时会增加延迟
* @param  PWR_Regulator: 设置停止模式时调压器的工作模式
*            @arg PWR_MainRegulator_ON: 调压器正常运行
*            @arg PWR_Regulator_LowPower: 调压器低功耗运行
* @param  PWR_STOPEntry: 设置使用WFI还是WFE进入停止模式
*            @arg PWR_STOPEntry_WFI: WFI进入停止模式
*            @arg PWR_STOPEntry_WFE: WFE进入停止模式
* @retval None
*/
void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry)
{
    uint32_t tmpreg = 0;
    /* 检查参数 */
    assert_param(IS_PWR_REGULATOR(PWR_Regulator));
    assert_param(IS_PWR_STOP_ENTRY(PWR_STOPEntry));

    /* 设置调压器的模式 ------------*/
    tmpreg = PWR->CR;
    /* 清除 PDDS 及 LPDS 位 */
    tmpreg &= CR_DS_MASK;
    /* 根据PWR_Regulator 的值(调压器工作模式)配置LPDS,MRLVDS及LPLVDS位*/
    tmpreg |= PWR_Regulator;
    /* 写入参数值到寄存器 */
    PWR->CR = tmpreg;
    /* 设置内核寄存器的SLEEPDEEP位 */
    SCB->SCR |= SCB_SCR_SLEEPDEEP;

    /* 设置进入停止模式的方式-----------------*/
    if (PWR_STOPEntry == PWR_STOPEntry_WFI) {
        /* 需要中断唤醒 */
        __WFI();
    } else {
        /* 需要事件唤醒 */
        __WFE();
    }

    /* 以下的程序是当重新唤醒时才执行的,清除SLEEPDEEP位的状态 */
    SCB->SCR &= (uint32_t)~((uint32_t)SCB_SCR_SLEEPDEEP);
}

这个函数有两个输入参数,分别用于控制调压器的模式及选择使用WFI或WFE停止,代码中先是根据调压器的模式配置PWR_CR寄存器, 再把内核寄存器的SLEEPDEEP位置1,这样再调用WFI或WFE命令时,W55MH32就不是睡眠,而是进入停止模式了。 函数结尾处的语句用于复位SLEEPDEEP位的状态,由于它是在WFI及WFE指令之后的,所以这部分代码是在W55MH32被唤醒的时候才会执行。

要注意的是进入停止模式后,W55MH32的所有I/O都保持在停止前的状态,而当它被唤醒时,W55MH32使用HSI作为系统时钟(8MHz)运行, 由于系统时钟会影响很多外设的工作状态,所以一般我们在唤醒后会重新开启HSE,把系统时钟设置回原来的状态。

2.4 进入待机模式

类似地,W55MH32标准库也提供了控制进入待机模式的函数,其定义见代码清单:电源管理-3 :

代码清单:电源管理-3 进入待机模式

/**
* @brief 进入待机模式
* @note   待机模式时,除以下引脚,其余引脚都在高阻态:
*          -复位引脚
*          - RTC_AF1 引脚 (PC13) (需要使能侵入检测、时间戳事件或RTC闹钟事件)
*          - RTC_AF2 引脚 (PI8) (需要使能侵入检测或时间戳事件)
*          - WKUP 引脚 (PA0) (需要使能WKUP唤醒功能)
* @note  在调用本函数前还需要清除WUF寄存器位
* @param  None
* @retval None
*/
void PWR_EnterSTANDBYMode(void)
{
    /* 清除 Wake-up 标志 */
    PWR->CR |= PWR_CR_CWUF;
    /* 选择待机模式 */
    PWR->CR |= PWR_CR_PDDS;
    /* 设置内核寄存器的SLEEPDEEP位  */
    SCB->SCR |= SCB_SCR_SLEEPDEEP;
    /* 存储操作完毕时才能进入待机模式,使用以下语句确保存储操作执行完毕 */
#if defined ( __CC_ARM   )
    __force_stores();
#endif
    /* 等待中断唤醒 */
    __WFI();
}

该函数中先配置了PDDS寄存器位及SLEEPDEEP寄存器位,接着调用__force_stores()函数确保存储操作完毕后再调用WFI指令, 从而进入待机模式。这里值得注意的是,待机模式也可以使用WFE指令进入的,如果您有需要可以自行修改。

在进入待机模式后,除了被使能了的用于唤醒的I/O,其余I/O都进入高阻态, 而从待机模式唤醒后,相当于复位W55MH32芯片,程序重新从头开始执行。

3 PWR—电源电压检测功能

3.1 代码分析

1. 头文件包含与全局变量定义

#include < stdlib.h >
#include < string.h >
#include < stdio.h >
#include "delay.h"
#include "w55mh32.h"

USART_TypeDef *USART_TEST = USART1;

包含了标准库的头文件以及自定义的 delay.h 和 w55mh32.h 头文件。

定义了一个指向 USART_TypeDef 结构体的指针 USART_TEST,并将其初始化为 USART1,用于后续的串口操作。

2. 函数声明

void UART_Configuration(uint32_t bound);
void PVD_Configuration(void);

声明了两个函数,UART_Configuration() 用于配置串口通信,PVD_Configuration()用于配置电源电压检测(PVD)功能。

3. main()函数

int main(void)
{
    RCC_ClocksTypeDef clocks;

    delay_init();
    UART_Configuration(115200);
    RCC_GetClocksFreq(&clocks);

    printf("n");
    printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, PCLK2: %3.1fMhz, ADCCLK: %3.1fMhzn",
           (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 PVD Test.n");

    PVD_Configuration();
    while (1);
}

定义了一个 RCC_ClocksTypeDef 类型的变量 clocks,用于存储系统时钟频率信息。

调用 delay_init()函数初始化延时功能。

调用 UART_Configuration()函数配置串口通信,波特率为 115200。

调用 RCC_GetClocksFreq()函数获取系统时钟频率信息,并通过串口打印出来。

调用 PVD_Configuration()函数配置电源电压检测功能。

进入无限循环,保持程序运行。

4. PVD_Configuration()函数

void PVD_Configuration(void)
{
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

    EXTI_ClearITPendingBit(EXTI_Line16);
    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_Init(&EXTI_InitStructure);

    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);

    PWR_PVDLevelConfig(PWR_PVDLevel_2V9);
    PWR_PVDCmd(ENABLE);
}

定义了 EXTI_InitTypeDef 和 NVIC_InitTypeDef 类型的变量,分别用于配置外部中断和嵌套向量中断控制器(NVIC)。

使能 PWR(电源控制)和 BKP(备份域)外设的时钟。

清除外部中断线 16 的中断挂起标志位。

配置外部中断线 16 为中断模式,触发方式为上升沿和下降沿触发,并使能该中断线。

配置 NVIC 的优先级分组为 1。

配置 PVD 中断的优先级,并使能该中断。

设置 PVD 的阈值为 2.9V,并使能 PVD 功能。

5. UART_Configuration()函数

void UART_Configuration(uint32_t bound)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate            = bound;
    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    USART_InitStructure.USART_Parity              = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;

    USART_Init(USART_TEST, &USART_InitStructure);
    USART_Cmd(USART_TEST, ENABLE);
}

定义了 GPIO_InitTypeDef 和 USART_InitTypeDef 类型的变量,分别用于配置 GPIO 和 USART。

使能 USART1 和 GPIOA 外设的时钟。

配置 GPIOA 的引脚 9 为复用推挽输出模式,用于 USART1 的发送功能;配置引脚 10 为浮空输入模式,用于 USART1 的接收功能。

配置 USART1 的波特率、数据位、停止位、奇偶校验等参数,并使能 USART1。

6. PVD_IRQHandler()函数

void PVD_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line16) != RESET)
    {
        if (PWR_GetFlagStatus(PWR_FLAG_PVDO) == SET)
        {
            printf("VDD Below the selected PVD thresholdn");
        }
        else
        {
            printf("VDD Above the selected PVD thresholdn");
        }

        EXTI_ClearITPendingBit(EXTI_Line16);
    }
}

这是 PVD 中断处理函数。

检查外部中断线 16 的中断标志位是否被置位。

如果 PVD 输出标志位(PWR_FLAG_PVDO)被置位,说明电源电压低于设定的阈值,通过串口输出相应信息;否则,说明电源电压高于设定的阈值,也通过串口输出相应信息。

清除外部中断线 16 的中断挂起标志位。

7. SER_PutChar() 和 fputc()函数

int SER_PutChar(int ch)
{
    while (!USART_GetFlagStatus(USART_TEST, USART_FLAG_TC));
    USART_SendData(USART_TEST, (uint8_t)ch);

    return ch;
}

int fputc(int c, FILE *f)
{
    /* Place your implementation of fputc here */
    /* e.g. write a character to the USART */
    if (c == 'n')
    {
        SER_PutChar('r');
    }
    return (SER_PutChar(c));
}

SER_PutChar()函数用于向串口发送一个字符,等待发送完成标志位被置位后再发送下一个字符。

fputc()函数是标准库函数 printf() 的底层实现,将字符发送到串口。如果遇到换行符 n,则先发送回车符 r。

这段代码通过配置 PVD 功能,实时监测电源电压,并在电压高于或低于设定阈值时触发中断,通过串口输出相应的提示信息。同时,还配置了串口通信功能,用于输出系统时钟频率信息和 PVD 监测结果。

3.2 下载验证

电压正常情况下不显示,想要查看可自行添加代码;如:

   while (1)
    {
        // 轮询检查电压状态(每1秒检查一次)
        delay_ms(1000); // 延时避免频繁查询
        
        if (PWR_GetFlagStatus(PWR_FLAG_PVDO) == SET)
        {
            printf("VDD Below PVD threshold (2.9V)n");
        }
        else
        {
            printf("VDD Normal (>= 2.9V)n"); // 电压正常
        }
    }

显示效果如下:

wKgZO2gxeqiASK1-AACHX-pvLBM091.png

4 待机及唤醒

4.1 代码解析

1. 头文件和全局变量

#include < stdlib.h >
#include < string.h >
#include < stdio.h >
#include "delay.h"
#include "w55mh32.h"

USART_TypeDef *USART_TEST = USART1;

包含了标准库头文件和自定义的头文件。

USART_TEST 是一个指向 USART1 的指针,用于后续的串口操作。

2. 函数声明

void    UART_Configuration(uint32_t bound);
void    GPIO_Configuration(void);
uint8_t GetCmd(void);

声明了三个函数:

UART_Configuration():用于配置串口。

GPIO_Configuration():用于配置 GPIO 引脚。

GetCmd():用于从串口获取用户输入的命令。

3. 主函数 main()

int main(void)
{
    RCC_ClocksTypeDef clocks;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

    delay_init();
    UART_Configuration(115200);
    RCC_GetClocksFreq(&clocks);

    printf("n");
    printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, PCLK2: %3.1fMhz, ADCCLK: %3.1fMhzn",
           (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 Standby Test.n");
    printf("Enable WakeUp Pin - PA0n");
    printf("Please Input 's', Come Standby Moden");

    PWR_WakeUpPinCmd(ENABLE);

    while (GetCmd() != 's');
    GPIO_Configuration();
    PWR_EnterSTANDBYMode();

    while (1);
}

时钟和外设使能:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); 使能电源管理外设(PWR)和备份寄存器(BKP)的时钟。

初始化操作:

delay_init():初始化延时函数。

UART_Configuration(115200):

配置串口波特率为 115200。

RCC_GetClocksFreq(&clocks):

获取系统时钟频率信息。

信息输出:

通过 printf()函数输出系统时钟频率信息,并提示用户可以通过输入 s 使设备进入待机模式。

唤醒引脚使能:

PWR_WakeUpPinCmd(ENABLE):使能唤醒引脚(PA0),用于从待机模式唤醒设备。

等待用户输入:

while (GetCmd() != 's');:循环等待用户从串口输入字符 s。

GPIO 配置和进入待机模式:

GPIO_Configuration():配置所有 GPIO 引脚为模拟输入模式。

PWR_EnterSTANDBYMode():使设备进入待机模式。

无限循环:while (1);

进入无限循环,实际上在进入待机模式后不会执行到这里。

4. GPIO_Configuration()函数

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_Init(GPIOA, &GPIO_InitStructure);
}

使能所有 GPIO 端口的时钟。

配置 GPIO 引脚的初始化结构体,将所有引脚设置为模拟输入模式,速度为 50MHz。

通过循环和 GPIO_Init()函数将所有 GPIO 端口的引脚都初始化为模拟输入模式。

5. UART_Configuration()函数

void UART_Configuration(uint32_t bound)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate            = bound;
    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    USART_InitStructure.USART_Parity              = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;

    USART_Init(USART_TEST, &USART_InitStructure);
    USART_Cmd(USART_TEST, ENABLE);
}

使能 USART1 和 GPIOA 的时钟。

配置 PA9 为复用推挽输出(用于 USART1 的发送),PA10 为浮空输入(用于 USART1 的接收)。

配置 USART1 的波特率、数据位、停止位、奇偶校验等参数,并使能 USART1。

6. GetCmd()函数

uint8_t GetCmd(void)
{
    uint8_t tmp = 0;

    if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE))
    {
        tmp = USART_ReceiveData(USART1);
    }
    return tmp;
}

检查 USART1 的接收缓冲区非空标志(USART_FLAG_RXNE)。

如果标志置位,表示接收到了数据,将数据从 USART1 的接收缓冲区读取到 tmp 变量中并返回。

7. SER_PutChar() 和 fputc()函数

int SER_PutChar(int ch)
{
    while (!USART_GetFlagStatus(USART_TEST, USART_FLAG_TC));
    USART_SendData(USART_TEST, (uint8_t)ch);

    return ch;
}

int fputc(int c, FILE *f)
{
    if (c == 'n')
    {
        SER_PutChar('r');
    }
    return (SER_PutChar(c));
}

SER_PutChar()函数用于将一个字符发送到串口,等待发送完成标志(USART_FLAG_TC)置位后再发送下一个字符。

fputc()函数是标准库 printf()函数的底层实现,在发送换行符(n)时会先发送回车符(r),以确保在串口终端上正确显示。

这段代码的主要功能是允许用户通过串口输入 s 命令使 W55MH32 设备进入待机模式,同时使能了唤醒引脚(PA0),可以在待机模式下通过该引脚唤醒设备。在进入待机模式前,将所有 GPIO 引脚配置为模拟输入模式以降低功耗。通过串口可以输出系统时钟频率信息和操作提示。

4.2 下载验证

输入‘s’进入待机模式:

wKgZPGgxeqiAARPfAAB0PkFOBVw671.png

把PA0引脚拉高,设备唤醒:

wKgZPGgxfhKAKxmXAAB2WGNCRpo561.jpg

为您推荐

Linux系统挂载新固态硬盘的方法,触觉智能工控主板演示

Linux系统挂载新固态硬盘的方法,触觉智能工控主板演示

本文介绍Linux系统下新固态硬盘的挂载,使用触觉智能RK3568工控主板演示(型号IDO-SBC3528) 检测固态...

第十九章 ADC——电压采集

第十九章 ADC——电压采集

单芯片解决方案,开启全新体验——W55MH32 高性能以太网单片机 W55MH32是WIZnet重磅推出的高性能以...

逆变焊机新时代:碳化硅(SiC)技术开启高效节能新篇章

逆变焊机新时代:碳化硅(SiC)技术开启高效节能新篇章

逆变焊机新时代:碳化硅(SiC)技术开启高效节能新篇章 引言:焊机行业的挑战与革新 在工业制造领域,焊接设备是...

DD3118国产USB3.0读卡芯片,工业级USB3.0读卡器IC可替代GL3213S方案

DD3118国产USB3.0读卡芯片,工业级USB3.0读卡器IC可替代GL3213S方案

抖胆科技推出的DD3118是一款无晶振的USB3.0 Dua/Single LUN卡读取器控制器,采用40纳米低功耗技...

Intel至强6:AI江湖的幕后大佬、NVIDIA B300的唯一伙伴

Intel至强6:AI江湖的幕后大佬、NVIDIA B300的唯一伙伴

随着生成式AI、预测式AI的浪潮一波高过一波,工作负载的类型越来越丰富、复杂度越来越高,对于AI服务器性能、能效的需求...

当前非电脑浏览器正常宽度,请使用移动设备访问本站!