| 本帖最后由 虚幻的是灵魂 于 2024-9-7 12:02 编辑 
 #申请原创# @21ic小喇叭 @21小跑堂
 一、PMU
 PMU全称Power Management Unit,电源管理单元。1、电源域 
 
 总共有三大电源域,包括VDD / VDDA域,1.2V域和备份域。2、VDD/VDDA域 VDD/VDDA域如下图: 提供PMU 常规电源供应以下模块的供电: 看门狗主频晶振内部晶振ADC和DACLDO电源转换上电复位锁相环
3、备份域
 备份域如下图: 备份域提供以下供电: 4、1.2V域
 1.2V域如下图所示: 这个作用域主要提供: AHB高速总线的供电APB外设总线的供电内存Cortex-M4的供电
二、模式介绍
 1、省电模式
 总共有三个省电模式: 2、睡眠模式
 睡眠模式时,会关闭 1.2V域中的 Cortex-M4的供电。3、深度睡眠模式 进入深度模式时,会关闭 1.2V域中的所有供电;同时关闭VDD/VDDA域中的HXTALIRC16MPLLs4、待机模式 
 进入待机模式时,会关闭1.2V域中的所有供电;同时关闭VDD/VDDA域中的LDO、IRC16M、HXTAL、PLLs;几种模式总结 
 | 模式 | 睡眠 | 深度睡眠 | 待机 |  | 描述 | 仅关闭 CPU 时钟 | 1、关闭 1.2V 电源域的所有时钟;2、关闭 IRC16M、HXTAL和 PLL | 1、关闭 1.2V 电源域的供电;2、关闭 IRC16M、HXTAL和 PLL |  | LDO状态 | 开启(正常功耗,正常驱动模式) | 开启(正常功耗或低功耗模式,正常驱动或低驱动模式) | 关闭 |  | 配置 | SLEEPDEEP=0 | SLEEPDEEP=1,STBMOD =0 | SLEEPDEEP=1,STBMOD=1,WURST=1 |  | 进入指令 | WFI或 WFE | WFI或 WFE | WFI或 WFE |  | 唤醒 | 若通过 WFI 进入,则任何中断均可唤醒;若通过 WFE 进入,则任何事件(或SEVONPEND=1时的中断)均可唤醒。 | 若通过 WFI进入,来自 EXTI的任何中断可唤醒;若通过WFE 进入,来自 EXTI 的任何事件(或 SEVONPEND =1 时的中断)可唤醒 | 1、NRST 引脚;2、WKUP 引脚;3、FWDGT 复位;4、RTC |  | 唤醒延迟 | 无 | IRC16M 唤醒时间,如果 LDO 处于低功耗模式,需增加 LDO 唤醒时间 | 上电序列 | 
 三、WFI和WFE指令
 在ARM架构中,WFI(Wait For Interrupt)和 WFE(Wait For Event)是用于使处理器进入低功耗状态的指令。这两个指令主要用于在空闲时暂停处理器的执行,以节省功耗。
 
 1、WFI指令:
 
 WFI 指令使处理器进入等待中断状态。当处理器执行到 WFI 时,它会进入低功耗模式,直到有一个中断请求到达,将处理器唤醒。在等待中断期间,处理器会停止执行指令,以减少功耗。
 
 2、WFE指令:
 
 WFE 指令与 WFI 类似,但它不仅能够等待中断,还能够等待事件。事件是由外部设备或其他处理器触发的信号。当执行到 WFE 时,处理器会进入低功耗模式,直到有中断或事件到达,将处理器唤醒。与 WFI 不同,WFE 可以等待中断或事件中的任何一个。
 
 四、案例需求
 
 说明:
 
 让LED1 每间隔一段时间闪烁(500ms)通过串口切换 省电模式
 
 说明:
 
 为KEY2配置外部中断按键,按下时LED2自动切换开关
 说明:
 
 为PA0配置外部中断按键,按下时LED3自动切换开关
五、模式初始化1、睡眠模式
 2、深度睡眠模式static void sleep_mode() {
    // 电池管理单元时钟
    rcu_periph_clock_enable(RCU_PMU);
    // 进入睡眠模式
    pmu_to_sleepmode(WFI_CMD);
 
}
3、待机模式static void deepsleep_mode() {
    // 电池管理单元时钟
    rcu_periph_clock_enable(RCU_PMU);
    // 进入深度睡眠模式
    pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_ENABLE, WFI_CMD);
 
}
源码:static void standby_mode() {
    // 电池管理单元时钟
    rcu_periph_clock_enable(RCU_PMU);
                pmu_wakeup_pin_enable();
    // 进入待机模式
                pmu_to_standbymode();
}
 
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "Usart0.h"
 
 
static void sleep_mode() {
    // 电池管理单元时钟
    rcu_periph_clock_enable(RCU_PMU);
    // 进入睡眠模式
    pmu_to_sleepmode(WFI_CMD);
 
}
 
static void deepsleep_mode() {
    // 电池管理单元时钟
    rcu_periph_clock_enable(RCU_PMU);
    // 进入深度睡眠模式
    pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_ENABLE, WFI_CMD);
 
}
 
static void standby_mode() {
    // 电池管理单元时钟
    rcu_periph_clock_enable(RCU_PMU);
                pmu_wakeup_pin_enable();
    // 进入待机模式
                pmu_to_standbymode();
}
 
void Usart0_on_recv(uint8_t* data, uint32_t len) {
    printf("recv: %s\r\n", data);
 
    if(data[0] == 0x00) {
        printf("sleep mode \r\n");
        sleep_mode();
    } else if(data[0] == 0x01) {
        printf("deepsleep mode \r\n");
        deepsleep_mode();
                                SystemInit();
    } else if(data[0] == 0x02) {
        printf("standby mode \r\n");
        standby_mode();
    } 
}
 
static void EXTI0_config() {
    uint32_t extix = EXTI_0;
    uint32_t extix_irq = EXTI0_IRQn;
    uint32_t extix_irq_pre = 1;
    uint32_t extix_irq_sub = 1;
 
    uint32_t extix_trig = EXTI_TRIG_BOTH;
 
    uint32_t extix_port_rcu = RCU_GPIOA;
    uint32_t extix_port = GPIOA;
    uint32_t extix_pin = GPIO_PIN_0;
    uint32_t extix_src_port = EXTI_SOURCE_GPIOA;
    uint32_t extix_src_pin = EXTI_SOURCE_PIN0;
 
    /*************** gpio ****************/
    // PA0,
    // 时钟初始化
    rcu_periph_clock_enable(extix_port_rcu);
    // 配置GPIO模式
    gpio_mode_set(extix_port, GPIO_MODE_INPUT, GPIO_PUPD_NONE, extix_pin);
 
    /*************** exti ****************/
    // 时钟配置
    rcu_periph_clock_enable(RCU_SYSCFG);
    // 配置中断源
    syscfg_exti_line_config(extix_src_port, extix_src_pin);
    // 中断初始化
    exti_init(extix, EXTI_INTERRUPT, extix_trig);
 
    // 配置中断优先级
    nvic_irq_enable(extix_irq, extix_irq_pre, extix_irq_sub);
    // 使能中断
    exti_interrupt_enable(extix);
    // 清除中断标志位
    exti_interrupt_flag_clear(extix);
}
 
static void EXTI1_config() {
    uint32_t extix = EXTI_1;
    uint32_t extix_irq = EXTI1_IRQn;
    uint32_t extix_irq_pre = 1;
    uint32_t extix_irq_sub = 1;
 
    uint32_t extix_trig = EXTI_TRIG_BOTH;
 
    uint32_t extix_port_rcu = RCU_GPIOD;
    uint32_t extix_port = GPIOD;
    uint32_t extix_pin = GPIO_PIN_1;
    uint32_t extix_src_port = EXTI_SOURCE_GPIOD;
    uint32_t extix_src_pin = EXTI_SOURCE_PIN1;
 
    /*************** gpio ****************/
    // PA0,
    // 时钟初始化
    rcu_periph_clock_enable(extix_port_rcu);
    // 配置GPIO模式
    gpio_mode_set(extix_port, GPIO_MODE_INPUT, GPIO_PUPD_NONE, extix_pin);
 
    /*************** exti ****************/
    // 时钟配置
    rcu_periph_clock_enable(RCU_SYSCFG);
    // 配置中断源
    syscfg_exti_line_config(extix_src_port, extix_src_pin);
    // 中断初始化
    exti_init(extix, EXTI_INTERRUPT, extix_trig);
 
    // 配置中断优先级
    nvic_irq_enable(extix_irq, extix_irq_pre, extix_irq_sub);
    // 使能中断
    exti_interrupt_enable(extix);
    // 清除中断标志位
    exti_interrupt_flag_clear(extix);
}
 
void EXTI0_IRQHandler() {
    // 判断寄存器状态
    if(SET == exti_interrupt_flag_get(EXTI_0)) {
        if(gpio_input_bit_get(GPIOA, GPIO_PIN_0) == SET)
        {
            /* 按键按下操作的功能 */
            gpio_bit_toggle(GPIOG, GPIO_PIN_3);
            printf("PA0 press!\r\n");
        } else {
            /* 按键松开操作的功能 */
            printf("PA0 release!\r\n");
        }
    }
    // 清除中断标志位
    exti_interrupt_flag_clear(EXTI_0);
}
 
void EXTI1_IRQHandler() {
    // 判断寄存器状态
    if(SET == exti_interrupt_flag_get(EXTI_1)) {
        if(gpio_input_bit_get(GPIOD, GPIO_PIN_1) == SET)
        {
            gpio_bit_toggle(GPIOD, GPIO_PIN_7);
            printf("key2 press!\r\n");
        } else {
            /* 按键松开操作的功能 */
            printf("key2 release!\r\n");
        }
    }
    // 清除中断标志位
    exti_interrupt_flag_clear(EXTI_1);
}
 
 
int main(void)
{
    systick_config();
    Usart0_init();
                EXTI0_config();
                EXTI1_config();
                
                // LED1
    rcu_periph_clock_enable(RCU_GPIOE);
    gpio_mode_set(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3);
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_3);
                // LED2
                rcu_periph_clock_enable(RCU_GPIOD);
    gpio_mode_set(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7);
    gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_7);
                // LED3
                rcu_periph_clock_enable(RCU_GPIOG);
    gpio_mode_set(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3);
    gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_3);
 
    while(1) {
        gpio_bit_toggle(GPIOE, GPIO_PIN_3);
        delay_1ms(500);
    }
}
 
 注意: 中断优先级配置为NVIC_PRIGROUP_PRE2_SUB2情况下: 串口的抢占优先级不能设置为0,否则系统无法正常睡眠深度睡眠的外部中断的抢占优先级必须设置为0或1,否则无法正常唤醒
 
 六、总结
 
 
 
 普通睡眠:任何中断都会唤醒。 深度睡眠: 唤醒中断睡眠优先级要高一些。 待机: 会重启,不会走下面。
 
 
 
 |