电源管理单元

电源管理单元(Power Management Unit, PMU)是用于管理芯片电源状态、低功耗模式、供电监控以及唤醒功能的重要模块。它通过多个功能和配置选项,使芯片能够平衡性能与功耗需求,非常适合应用于低功耗设计中。

F407MPUdygldy

省电模式

省电模式是指处理器为了降低功耗而设计的一系列低功耗状态。这些模式通过暂停或关闭处理器的部分功能、时钟信号或供电,来减少功耗,延长设备的电池寿命。

睡眠模式

Sleep mode (Cortex®-M4 with FPU core stopped, peripherals kept running)

描述
Sleep 模式中,Cortex®-M4 内核(包括浮点单元 FPU)停止运行,但外设和时钟继续运行。此模式适合需要外设保持活跃、但允许处理器暂停任务的场景。

任何唤醒事件都可以唤醒系统(如果SEVONPEND 为1,任何中断都可以唤醒系统,请参考Cortex®-M4技术手册)。

特点

  • CPU 停止工作:通过执行 WFI(Wait For Interrupt) 或 WFE(Wait For Event)指令进入。
  • 外设继续运行:所有启用的外设(如 UART、I2C、ADC 等)保持正常工作。
  • 快速唤醒:唤醒时间极短,仅需几个时钟周期即可恢复到运行模式。
  • 功耗:相较运行模式功耗大幅降低,但高于 Stop 和 Standby 模式。

深度睡眠模式

深度睡眠模式又叫停止模式(Stop mode (all clocks are stopped))

描述
Stop 模式中,基于 Cortex®-M4 架构的的所有时钟(包括 1.2 供电域、系统时钟、外设时钟、PLL)都被停止,进入更深层次的低功耗状态。RAM 和寄存器内容会保持不变。

刚退出深度睡眠模式时,IRC16M 被选中作为系统时钟。请注意,如 果 LDO 工作在低功耗模式,那么唤醒时需额外的延时时间。

特点

  • 所有时钟停止:主时钟 HCLK、AHB/APB 总线时钟以及外设时钟完全停止。
  • RAM 和寄存器内容保持:支持从停止模式快速恢复。
  • 低功耗:相比 Sleep 模式功耗更低。
  • 唤醒方式:
    • 外部中断(例如:GPIO 引脚或 EXTI)。
    • RTC 闹钟中断或唤醒计时器。
  • 恢复时间较短:由于无需重新配置寄存器和时钟,恢复速度比 Standby 模式快。

待机模式

Standby mode (1.2 V domain powered off)

描述
Standby 模式是 Cortex®-M4 架构最深度低功耗模式,整个 1.2V 供电区域被断电来达到最低功耗(同时 LDO 和包括 IRC16M、HXTAL 和 PLL 也会被关闭)。此模式下,系统状态信息几乎全部丢失,仅保留备用域供电。

退出待机模式时,会发生上电复位,复位 之后 Cortex®-M4 将从 0x00000000 地址开始执行指令代码。

特点

  • 核心电源关闭:1.2 V 电源域(VCORE)完全断电,仅保持备用域供电(如 RTC 和备份寄存器)。
  • 所有时钟停止:系统所有时钟(包括外设时钟)停止。
  • 数据丢失:
    • RAM 和大部分寄存器内容丢失。
    • 仅 RTC 和备用寄存器(Backup Domain)保存数据。
  • 唤醒方式:
    • 通过外部信号(例如 WKUP 引脚)。
    • RTC 闹钟。
  • 恢复时间较长:需要重新初始化所有寄存器和时钟。

在 ARM 架构中,部分处理器内核(如 Cortex-M4 和 Cortex-M7)集成了 FPU,用于高效处理浮点运算,从而提升运算速度并减少处理器负载。

三种模式对比

模式 描述 功耗 时钟状态 内存和寄存器状态 唤醒时间 唤醒方式 适用场景
Sleep 模式 Cortex®-M4 内核停止,外设和时钟继续运行 较低 系统时钟和外设时钟保持运行 RAM 和寄存器保持 极短(几个时钟周期) 任何中断或事件(如外设中断、GPIO 中断) 系统需快速响应中断,但内核无需持续运行的场景。
Stop 模式 所有时钟停止,进入深度低功耗状态 更低 所有系统时钟和外设时钟完全停止 RAM 和寄存器保持 较短 外部中断(如 GPIO、EXTI)、RTC 闹钟 中长时间低功耗待机,需保持内存状态且快速恢复的场景。
Standby 模式 核心电源关闭,仅保留备用域供电,达到最低功耗 最低 所有系统时钟和外设时钟停止 RAM 和寄存器丢失,仅备份域保存数据 较长 外部中断(如 WKUP 引脚)、RTC 闹钟 超长时间待机,追求最低功耗且无需保存内存状态的场景。

刚退出深度睡眠模式时,IRC16M 被选中作为系统时钟。请注意,如 果 LDO 工作在低功耗模式,那么唤醒时需额外的延时时间。

WFI 和 WFE 指令

在 ARM Cortex-M 架构中,WFIWFE 是两条用于低功耗管理的指令,它们主要用于让处理器进入等待状态,以降低功耗。

不同的进入指令通常对应着不同的换新方式;

WFI

  • WFI 指令会让处理器进入低功耗状态,直到出现中断请求将其唤醒。

  • 通常用于等待下一次中断的触发。

唤醒条件

  • 有中断请求时唤醒。
  • 如果中断已被屏蔽,但 SEVONPEND 位设置为 1,也可以唤醒。

操作逻辑

  • 如果没有中断发生,处理器保持休眠;
  • 如果中断发生,处理器退出低功耗模式并执行中断服务程序。

WFE

  • WFE 指令会让处理器进入低功耗状态,直到检测到事件信号。

  • 与 WFI 不同,WFE 不仅可以通过中断唤醒,还可以通过事件唤醒。

唤醒条件

  • 有中断请求(中断标志置位)。
  • 外部事件或 SEVONPEND 被设置为 1。

操作逻辑

  • 如果事件寄存器为 1,WFE 指令立即返回,事件寄存器清零。
  • 如果事件寄存器为 0,处理器进入低功耗模式,直到事件信号到来。

事件和中断

中断是一种异步信号,通常由外设或内部系统异常触发,用于通知处理器需要立即执行特定任务。

事件是一种同步信号,用于在系统中传递较低优先级的触发信息。事件不会打断处理器的正常工作,而是依靠事件寄存器记录状态。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "USART0.h"
#include "EXTI.h"

void EXTI_on_trig(exti_line_enum linex) {
if(linex == EXTI_1){
printf("Key Trig\n");
}
}

void sleep_mode(){ // 睡眠模式
// PMU -> RCU
rcu_periph_clock_enable(RCU_PMU);
printf("sleepmode1\n");
// sleep mode
pmu_to_sleepmode(WFI_CMD);
printf("sleepmode2\n");
}

void deepsleep_mode() { // 深度睡眠模式
// PMU -> RCU
rcu_periph_clock_enable(RCU_PMU);
printf("deepsleep 1\n");
// deepsleep
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_ENABLE, WFI_CMD);
// 把主频设置回来,当被唤醒后会从此处开始执行
SystemInit();

printf("deepsleep 2\n");
}

void standby_mode(){ // 待机模式
// PMU -> RCU
rcu_periph_clock_enable(RCU_PMU);
/* 清理待机模式标记 */
pmu_flag_clear(PMU_FLAG_RESET_STANDBY);
/* 启用唤醒按钮 enable PMU wakeup pin */
pmu_wakeup_pin_enable();
printf("standby 1\n");
// standby待机模式
pmu_to_standbymode();
printf("standby 2\n");
}

void USART0_on_recv(uint8_t* data, uint32_t len) {
printf("recv: %s\n", data);

switch(data[0]) {
case 0x00: // 睡眠模式
sleep_mode();
break;
case 0x01: // 深度睡眠模式
deepsleep_mode();
break;
case 0x02: // 待机模式
standby_mode();
break;
default:
break;
}
}

static void GPIO_config(){
// 初始化GPIO PB2
rcu_periph_clock_enable(RCU_GPIOB);
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_2);
}

static void delay() {
uint32_t i = 50000000;
while(i--){
__NOP();
}
}

int main(void) {
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
USART0_init();
EXTI_init();
GPIO_config();
printf("Main Init\n");

while(1) {
// 让PB2切换亮灭
gpio_bit_toggle(GPIOB, GPIO_PIN_2);
// 使用自己的睡眠函数
delay();
}
}