具体原理可以看我的上一篇
硬件准备
我们的硬件平台是基于GD32F4xx微控制器的开发板,该微控制器支持多种电源管理模式。我们将使用以下硬件资源:
- LED连接到PB2引脚:用于闪烁指示。
- 串口:通过串口切换 省电模式
- 外部中断引脚: 为KEY2配置外部中断按键,按下时LED1自动切换开关
代码实现
1. 引入必要的头文件
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "USART0.h"
#include "EXTI.h"
这些头文件包含了必要的库和定义,用于访问GPIO、系统时钟、中断等硬件资源。
2. 任务目标
我们的目标是:
- 让LED(连接在PB2引脚)闪烁。
- 通过接收串口消息进入不同的节能模式:
0x00
:睡眠模式(任意中断可唤醒)0x01
:深度睡眠模式(EXTI外部中断可唤醒)0x02
:待机模式(4个特殊唤醒方式)
3. 实现节能模式
睡眠模式
void sleep_mode(){
// RCU -------------------
rcu_periph_clock_enable(RCU_PMU);
printf("sleep_mode:1\n");
// 进入睡眠模式
pmu_to_sleepmode(WFI_CMD);
// 唤醒中断的抢占优先级要比串口高才能唤醒
// 因为此代码目前阻塞在串口的中断函数里
printf("sleep_mode:2\n");
}
此函数使能PMU时钟,并调用pmu_to_sleepmode()
进入睡眠模式。通过WFI_CMD
指令等待中断唤醒。
深度睡眠模式
// 深度睡眠模式
void deep_sleep_mode(){
rcu_periph_clock_enable(RCU_PMU);
printf("deep_sleep_mode:1\n");
// 进入深度睡眠模式
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_ENABLE, WFI_CMD);
// 把主频设置回来
SystemInit();
printf("deep_sleep_mode:2\n");
}
此函数使能PMU时钟,并调用pmu_to_deepsleepmode()
进入深度睡眠模式。通过WFI_CMD
指令等待中断唤醒,并在退出深度睡眠后重置系统时钟。
待机模式
// 待机模式
void standby_mode(){
rcu_periph_clock_enable(RCU_PMU);
/* 清理待机模式标记 */
pmu_flag_clear(PMU_FLAG_RESET_STANDBY);
/* 启用唤醒按钮 */
pmu_wakeup_pin_enable();
printf("standby mode:1\n");
/* 进入待机模式 */
pmu_to_standbymode();
printf("standby mode:2\n");
}
此函数使能PMU时钟,清除待机模式标记,并启用唤醒引脚。调用pmu_to_standbymode()
进入待机模式。
4. 外部中断处理
void EXTI_1_on_trig(){
printf("EXTI_1\n");
}
此函数处理外部中断事件,当外部中断触发时,打印一条消息。
5. 串口接收处理
void USART0_on_recv(uint8_t* buffer, uint32_t len) {
printf("recv[%d]-> %s\n", len, buffer);
switch (buffer[0]) {
case 0x00:
sleep_mode();
break;
case 0x01:
deep_sleep_mode();
break;
case 0x02:
standby_mode();
break;
default:
break;
}
}
此函数处理接收到的串口数据,并根据接收到的命令字节调用相应的节能模式函数。
6. GPIO配置
static void GPIO_config(rcu_periph_enum rcu, uint32_t port, uint32_t pin) {
rcu_periph_clock_enable(rcu);
gpio_mode_set(port, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, pin);
gpio_output_options_set(port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, pin);
gpio_bit_write(port, pin, RESET);
}
此函数用于配置GPIO引脚为输出模式,并设置默认输出电平为低电平。
7. 延时函数
void delay(){
uint32_t i = 20000000;
while(i--) __NOP();
}
此函数通过循环计数来实现延时效果。
8. 主函数
int main(void) {
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
// systick_config();
EXTI_init();
USART0_init();
printf("init\n");
GPIO_config(RCU_GPIOB, GPIOB, GPIO_PIN_2);
while(1) {
gpio_bit_toggle(GPIOB, GPIO_PIN_2);
// delay_1ms(500);
delay();
}
}
主函数初始化了NVIC优先级组、外部中断、串口,并配置了LED对应的GPIO引脚。然后进入一个无限循环,不断切换LED的状态,并调用延时函数来实现500毫秒的闪烁周期。
注意事项
在中断优先级配置为NVIC_PRIGROUP_PRE2_SUB2
的情况下:
-
串口中断优先级:
- 串口中断的抢占优先级不能设置为0,否则系统可能无法正常进入睡眠模式,或者在睡眠模式下无法被正确唤醒。
-
深度睡眠模式下的外部中断优先级:
- 深度睡眠模式下的外部中断的抢占优先级需要设置为0或1(比串口中断的抢占优先级高),否则系统可能无法通过外部中断正确唤醒。
结论
通过上述代码,我们实现了基于GD32F4xx的嵌入式系统中的LED闪烁功能,并通过串口命令切换到了不同的节能模式。此外,还展示了如何通过外部中断唤醒系统。这些功能在实际应用中非常有用,尤其是在需要长期运行且功耗敏感的设备中