第17章 待机唤醒实验
第十七章 待机唤醒实验
1. 硬件设计
本实验用到的硬件资源有:
-
指示灯 DS0
-
KEY_UP 按键
-
TFTLCD 模块
本章,我们使用了 KEY_UP 按键用于唤醒和进入待机模式。然后通过 DS0 和 TFTLCD 模块来指示程序是否在运行。 这几个硬件的连接前面均有介绍。
2. 软件设计
2.1 检测外部WK_UP宏定义
#define WKUP_KD HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) //PA0 检测是否外部WK_UP按键按下
2.2 使系统进入待机模式
//系统进入待机模式
void Sys_Enter_Standby(void)
{
__HAL_RCC_AHB1_FORCE_RESET(); // 复位所有IO口
while(WKUP_KD); // 等待WK_UP按键松开(在有RTC中断时,必须等WK_UP松开再进入待机)
__HAL_RCC_PWR_CLK_ENABLE(); // 使能PWR时钟
__HAL_RCC_BACKUPRESET_FORCE(); // 复位备份区域
HAL_PWR_EnableBkUpAccess(); // 后备区域访问使能
// STM32F4,当开启了RTC相关中断后,必须先关闭RTC中断,再清中断标志位,然后重新设置
// RTC中断,再进入待机模式才可以正常唤醒,否则会有问题.
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB); // 清除待机模式标志位
__HAL_RTC_WRITEPROTECTION_DISABLE(&RTC_Handler); // 关闭RTC写保护
//关闭RTC相关中断,可能在RTC实验打开了
__HAL_RTC_WAKEUPTIMER_DISABLE_IT(&RTC_Handler, RTC_IT_WUT); // 关闭RTC WakeUpTimer中断
__HAL_RTC_TIMESTAMP_DISABLE_IT(&RTC_Handler, RTC_IT_TS); // 关闭RTC TimeStamp中断
__HAL_RTC_ALARM_DISABLE_IT(&RTC_Handler, RTC_IT_ALRA|RTC_IT_ALRB); // 关闭RTC Alarm中断
//清除RTC相关中断标志位
__HAL_RTC_ALARM_CLEAR_FLAG(&RTC_Handler,RTC_FLAG_ALRAF|RTC_FLAG_ALRBF); // 清除RTC Alarm中断标志位
__HAL_RTC_TIMESTAMP_CLEAR_FLAG(&RTC_Handler,RTC_FLAG_TSF); // 清除RTC TimeStamp中断标志位
__HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&RTC_Handler,RTC_FLAG_WUTF); // 清除RTC WakeUpTimer中断标志位
__HAL_RCC_BACKUPRESET_RELEASE(); // 备份区域复位结束
__HAL_RTC_WRITEPROTECTION_ENABLE(&RTC_Handler); // 使能RTC写保护
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); // 清除Wake_UP标志
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1); // 设置WKUP用于唤醒
HAL_PWR_EnterSTANDBYMode(); // 进入待机模式
}
在 void Sys_Enter_Standby(void)函数里面,我们要在进入待机模式前把所有开启的外设全部关闭,我们这里仅仅复位了所有的 IO 口,使得 IO 口全部为浮空输入。其他外设(比如ADC 等), 大家根据自己所开启的情况进行一一关闭就可,这样才能达到最低功耗! 然后我们调用函数 __HAL_RCC_PWR_CLK_ENABLE()来使能PWR时钟,调用函数HAL_PWR_EnableWakeUpPin 用来设置 WK_UP 引脚作为唤醒源。最后调用 HAL_PWR_EnterSTANDBYMode 函数进入待机模式。
2.3 检测WK_UP引脚的信号
// 检测WKUP脚的信号
// 返回值1:连续按下3s以上
// 0:错误的触发
u8 Check_WKUP(void)
{
u8 t = 0; // 记录按下的次数
u8 tx = 0;// 记录松开的次数
LED0 = 0; //亮灯DS0
while(1)
{
if(WKUP_KD) // 已经按下了
{
t++; // 按下次数+1
tx = 0; // 松开次数清零
}
else
{
tx++; // 松开次数+1
if(tx>3) // 超过90ms内没有WKUP信号
{
LED0 = 1; // 灭灯DS0,表示错误的触发
return 0; // 错误的按键,按下次数不够
}
}
delay_ms(30);
if(t >= 100) // 按下超过3秒钟
{
LED0=0; // 点亮DS0
return 1; // 按下3s以上了
}
}
}
2.4 中断处理相关函数
//外部中断线0中断服务函数
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
// 中断线0中断处理过程
// 此函数会被HAL_GPIO_EXTI_IRQHandler()调用
// GPIO_Pin:引脚
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_0) // PA0
{
if(Check_WKUP()) // 检测到WKUP长时间按下按键
{
Sys_Enter_Standby(); // 进入待机模式
}
}
}
2.5 WK_UP唤醒初始化
//PA0 WKUP唤醒初始化
void WKUP_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); // 开启GPIOA时钟
GPIO_Initure.Pin = GPIO_PIN_0; // PA0
GPIO_Initure.Mode = GPIO_MODE_IT_RISING; // 中断,上升沿
GPIO_Initure.Pull = GPIO_PULLDOWN; // 下拉
GPIO_Initure.Speed = GPIO_SPEED_FAST; // 快速
HAL_GPIO_Init(GPIOA, &GPIO_Initure);
//检查是否是正常开机
if(Check_WKUP() == 0)
{
Sys_Enter_Standby(); // 不是开机,进入待机模式
}
HAL_NVIC_SetPriority(EXTI0_IRQn,0x02,0x02);//抢占优先级2,子优先级2
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
在 void WKUP_Init(void)函数里面, 我们首先要使能 GPIOA 时钟,同时因为我们要使
用到外部中断,所以必须先使能 SYSCFG 时钟。然后对 GPIOA 初始化位下拉输入。同时调用函数 SYSCFG_EXTILineConfig 配置 GPIOA.0 连接到中断线 0。最后初始化 EXTI 中断线以及NVIC 中断优先级。
在上面初始化的过程中, 我们还先先判断WK_UP 是否按下了 3 秒钟,来决定要不要开机,如果没有按下 3 秒钟,程序直接就进入了待机模式。所以在下载完代码的时候,是看不到任何反应的。我们必须先按 WK_UP 按键 3 秒开机,才能看到 DS0 闪烁。
2.6 主函数
int main(void)
{
HAL_Init(); // 初始化HAL库
Stm32_Clock_Init(336,8,2,7); // 设置时钟,168Mhz
delay_init(168); // 初始化延时函数
uart_init(115200); // 初始化USART
usmart_dev.init(84); // 初始化USMART
LED_Init(); // 初始化LED
LCD_Init(); // 初始化LCD
WKUP_Init(); // 待机唤醒初始化
POINT_COLOR = RED; // 设置字体颜色为红色
LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
LCD_ShowString(30,70,200,16,16,"WKUP TEST");
LCD_ShowString(30,130,200,16,16,"WK_UP:Stanby/WK_UP");
while(1)
{
LED0=!LED0;
delay_ms(250); //延时250ms
}
}
3. 小结
实验目的
- 理解 STM32F407 的待机唤醒机制。
- 测试系统在待机状态下的功耗。
- 通过外部按键实现设备的唤醒。
下面是一个完整的代码示例:
#include "stm32f4xx_hal.h"
// 定义 LED 引脚
#define LED_PIN GPIO_PIN_13
#define LED_PORT GPIOC
// 定义 WKUP 按键引脚
#define WKUP_PIN GPIO_PIN_0
#define WKUP_PORT GPIOA
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
void enterSleepMode(void);
void wakeUp(void);
int main(void) {
HAL_Init(); // 初始化 HAL 库
SystemClock_Config(); // 配置系统时钟
MX_GPIO_Init(); // 初始化 GPIO
// 初始状态,点亮 LED
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);
while (1) {
enterSleepMode(); // 进入待机模式
// 等待 WKUP 按键中断唤醒
}
}
void enterSleepMode() {
// 关闭 LED,准备进入待机模式
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);
// 进入待机模式
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); // 清除唤醒标志
HAL_PWR_EnterSTANDBYMode(); // 进入待机模式
}
void wakeUp() {
// 唤醒后点亮 LED
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == WKUP_PIN) {
wakeUp(); // 按键中断处理
}
}
// GPIO 初始化
static void MX_GPIO_Init(void) {
__HAL_RCC_GPIOC_CLK_ENABLE(); // 使能 GPIOC 时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能 GPIOA 时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
// LED 引脚配置
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 不使用上拉或下拉电阻
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
// WKUP 按键配置
GPIO_InitStruct.Pin = WKUP_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿中断
GPIO_InitStruct.Pull = GPIO_PULLDOWN; // 使用下拉电阻
HAL_GPIO_Init(WKUP_PORT, &GPIO_InitStruct);
// 启用中断
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); // 设置优先级
HAL_NVIC_EnableIRQ(EXTI0_IRQn); // 使能中断
}
// 系统时钟配置(根据具体的时钟配置进行调整)
void SystemClock_Config(void) {
// 此处根据具体应用进行时钟配置
}
void EXTI0_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(WKUP_PIN); // 处理外部中断
}