第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); // 处理外部中断
}
posted @ 2024-10-12 13:47  hazy1k  阅读(10)  评论(0编辑  收藏  举报