1 Star 9 Fork 2

网易独家音乐人Mike Zhou/基于HAL库建立自己的低功耗模式配置库(STM32L4系列低功耗所有配置汇总)

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
LOW_POWER.c 12.67 KB
一键复制 编辑 原始数据 按行查看 历史
#include "stm32l4xx_hal.h"
#include "LOW_POWER.h"
LOW_POWER_Entry_Cfg LP_Entry_Cfg={0};
/*!
* @brief 重置GPIO(都会进行),或再将除外部高低速晶振复用、SWCLK、SWDIO复用的所有GPIO配置为模拟输入(false)
* 注意:用于串口唤醒等的引脚,不可配置为模拟输入,也不可关闭
* 在进行GPIO初始化前,先将GPIO_DeInit,但是不做也不影响,不过还是建议跑一下
* 以优先级顺序来看:
* 如果这一组GPIO都没用到过 那么直接不开启时钟就最省电
* 如果这一组GPIO有引脚用过了 时钟不能关 那么就将用过的引脚配置为模拟输入
* 切记!!!:
* 不要将没用过的引脚配置为模拟输入 耗电量其实会稍微增加一点!
* 不要将没用过的GPIO时钟打开以后再配置为模拟输入 耗电量会增加很多 就算配置后再关时钟也没用!
* 尽量不要勾选CubeMX中的配置闲置引脚为模拟输入的选项 没用到的时钟还开启了会增加很多耗电
* 低功耗模式配置:
* 在进入STOP模式时 GPIO会保留原本的状态 所以把开启后不需要再保留的GPIO配置为模拟输入确实省电 时钟的话不用的肯定关 其他的反正都会关(除了保留的时钟)
* 在进入SLEEP模式时 时钟并不会关闭 所以时钟应手动关闭 且将开启后的GPIO配置为模拟输入
* 待机模式和关机模式就更不用在意GPIO口耗电了
* https://blog.csdn.net/weixin_53403301/article/details/129055530
*
* @param [in] EnableNotDisable: 使所有GPIO变成模拟输入或不进行模拟配置
*
* @return None
*/
__weak void GPIO_Reset_Init(bool EnableNotDisable)
{
// HAL_GPIO_DeInit(GPIOA,GPIO_PIN_2|GPIO_PIN_3); //用于串口唤醒的引脚 不可变动
/*
HAL_GPIO_DeInit(GPIOA,GPIO_PIN_0|GPIO_PIN_1
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7
|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12|GPIO_PIN_15);
HAL_GPIO_DeInit(GPIOB,GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10
|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
|GPIO_PIN_15|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5
|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9);
HAL_GPIO_DeInit(GPIOC,GPIO_PIN_13|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2
|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6
|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10
|GPIO_PIN_11|GPIO_PIN_12);
HAL_GPIO_DeInit(GPIOD,GPIO_PIN_2);
HAL_GPIO_DeInit(GPIOH,GPIO_PIN_3);
*/
if(EnableNotDisable)
{
/*
GPIO_InitTypeDef GPIO_InitStruct = {0};
*/
/* GPIO Ports Clock Enable */
/*
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
*/
/*Configure GPIO pins : PC13 PC0 PC1 PC2
PC3 PC4 PC5 PC6
PC7 PC8 PC9 PC10
PC11 PC12 */
/*
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2
|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6
|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10
|GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
*/
/*Configure GPIO pins : PA0 PA1 PA2 PA3
PA4 PA5 PA6 PA7
PA8 PA9 PA10 PA11
PA12 PA15 */
/*
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7
|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
*/
// //用于串口唤醒的 不可变动
// GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
// GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
// GPIO_InitStruct.Pull = GPIO_NOPULL;
// HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PB0 PB1 PB2 PB10
PB11 PB12 PB13 PB14
PB15 PB3 PB4 PB5
PB6 PB7 PB8 PB9 */
/*
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10
|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
|GPIO_PIN_15|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5
|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
*/
/*Configure GPIO pin : PD2 */
/*
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
*/
/*Configure GPIO pin : PH3 */
/*
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
*/
}
}
/*!
* @brief 所有外设初始化配置,根据使用需求来写
*
* @param [in] EnableNotDisable: 使能或者关闭
* true: 进行初始化外设(不包含时钟初始化)
* false: 或者关闭所有外设,所有GPIO配置为无上拉下拉且模拟输入,仅保留系统时钟和系统所需的GPIO口复用
* 该函数在进入低功耗前调用(false)
* 建议在进入该函数前(false)先配置用于唤醒的外设 如指定UART或RTC作为唤醒使用 然后再调用该函数 且不能关闭有唤醒功能的外设
* 若用于唤醒后的初始化,则建议先初始化时钟,再执行该函数的初始化(true)
* 在休眠期间使用的外设,不要关闭,也不要关闭GPIO等;相反,外设和GPIO等建议同时关闭(避免出现bug,并且也省电)
* 未关闭,但唤醒时重复初始化外设并不受影响
* 若未关闭的外设在运行中改变了初始化值,则建议不在唤醒时运行该初始化(前提是外设的GPIO等也没有作改动)
* 若需要在初始化后更改初始化值,则建议要么不进行初始化且不关闭(也包括GPIO等),或重新设置新值
*
* @return None
*/
__weak void PWR_Device_Init(bool EnableNotDisable)
{
if(EnableNotDisable)
{
//这里是系统最初的初始化值
GPIO_Reset_Init(false); //重置GPIO
/*
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_UART4_Init();
MX_ADC1_Init();
MX_ADC2_Init();
MX_TIM6_Init();
MX_RTC_Init();
MX_ADC3_Init();
*/
//这里放初始化后还要更改的配置,若要重新初始化,建议先运行外设DeInit
// HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8,GPIO_PIN_SET);
// HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4,GPIO_PIN_SET);
}
else
{
/*
// HAL_ADC_DeInit(&hadc1);
// HAL_ADC_DeInit(&hadc2);
// HAL_ADC_DeInit(&hadc3);
// HAL_UART_DeInit(&huart2); //唤醒用的串口 最好不要关闭:若不用于唤醒 则可以关闭 GPIO等同步关闭;若用于唤醒 则不能关闭 GPIO等也不能关闭
// HAL_UART_DeInit(&huart4);
// HAL_TIM_Base_DeInit(&htim6);
// HAL_RTC_DeInit(&hrtc); //唤醒用的RTC 最好不要关闭
*/
GPIO_Reset_Init(true); //GPIO配置为复用
}
}
/*!
* @brief 配置串口在停止模式下的唤醒
*
* @param [in] UART_Cfg: UART配置
*
* @return None
*/
__weak uint8_t Ctrl_UART_StopMode_WakeUp(LOW_POWER_UART_Cfg *UART_Cfg,bool EnableNotDisable)
{
if (!UART_Cfg->uart_handle)
{
return 0;
}
if(EnableNotDisable)
{
__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI); //保留唤醒用的HSI线 串口初始化时钟也必须要配置为HSI
HAL_UARTEx_StopModeWakeUpSourceConfig(UART_Cfg->uart_handle,UART_Cfg->UART_WakeUpStruct);
__HAL_UART_ENABLE_IT(UART_Cfg->uart_handle,UART_IT_WUF); //开启唤醒中断
HAL_UARTEx_EnableClockStopMode(UART_Cfg->uart_handle);
HAL_UARTEx_EnableStopMode(UART_Cfg->uart_handle); //开启模式
}
else
{
__HAL_UART_DISABLE_IT(UART_Cfg->uart_handle,UART_IT_WUF); //关闭唤醒中断
HAL_UARTEx_DisableClockStopMode(UART_Cfg->uart_handle);
HAL_UARTEx_DisableStopMode(UART_Cfg->uart_handle); //关闭模式
}
return 1;
}
/*!
* @brief 配置停止模式下的外设唤醒函数 true为开启 false为关闭 (不包含RTC唤醒)
*
* @return None
*/
__weak void Ctrl_Stop_Mode_WakeUp_Device(bool EnableNotDisable)
{
uint8_t i=0;
if(EnableNotDisable)
{
for(i=0;i<sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg)/sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg[0]);i++)
{
if(!Ctrl_UART_StopMode_WakeUp(&LP_Entry_Cfg.Device_Cfg.UART_Cfg[i],LP_Entry_Cfg.Device_Cfg.UART_Cfg[i].EnableNotDisable))
{
break;
}
}
}
else
{
for(i=0;i<sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg)/sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg[0]);i++)
{
if(!Ctrl_UART_StopMode_WakeUp(&LP_Entry_Cfg.Device_Cfg.UART_Cfg[i],false))
{
break;
}
}
}
}
/*!
* @brief 配置RTC在低功耗模式下的唤醒
*
* @param [in] RTC_Cfg: RTC配置
*
* @return None
*/
__weak void Ctrl_RTC_WakeUp(LOW_POWER_RTC_Cfg *RTC_Cfg,bool EnableNotDisable)
{
if(EnableNotDisable)
{
HAL_RTCEx_SetWakeUpTimer_IT(RTC_Cfg->rtc_handle,RTC_Cfg->counter,RTC_Cfg->clock);
}
else
{
__HAL_RTC_WAKEUPTIMER_EXTI_DISABLE_IT();
}
}
/*
进入低功耗的函数中 每个模式都有一个10ms的消抖 其实这个可以省略
但我们用的是HAL库 HAL库初始化时钟以后 会打开SysTick中断 如果这个开着 就一直无法进入低功耗(进入就被中断唤醒)
所以我这里加了个我自己写的延时函数 其中就有关闭SysTick的语句
如果你不用这个消抖 没啥BUG也行 但我还是建议加上去 毕竟也不差那10ms
*/
/*!
* @brief 进入低功耗模式
*
* @return None
*/
__weak void Enter_Low_PWR(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
switch(LP_Entry_Cfg.mode_flag)
{
case 0:
{
printf("[INFO] 不进入低功耗模式\n");
break;
}
case 1:
{
printf("[INFO] 进入睡眠模式\n");
delay_ms(10); //消抖
PWR_Device_Init(false);
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,LP_Entry_Cfg.SLEEPEntry_Cfg.SLEEPEntry);
__HAL_RCC_PWR_CLK_ENABLE();
Ctrl_RTC_WakeUp(NULL,false);
PWR_Device_Init(true);
break;
}
case 2:
{
printf("[INFO] 进入停止模式\n");
delay_ms(10); //消抖
PWR_Device_Init(false);
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
Ctrl_Stop_Mode_WakeUp_Device(true);
Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,LP_Entry_Cfg.STOPEntry_Cfg.STOPEntry);
__HAL_RCC_PWR_CLK_ENABLE();
LP_Entry_Cfg.SystemClock_Config_Fxn();
Ctrl_Stop_Mode_WakeUp_Device(false);
Ctrl_RTC_WakeUp(NULL,false);
PWR_Device_Init(true);
break;
}
case 3:
{
printf("[INFO] 三秒后进入待机模式\n");
delay_ms(3000);
printf("[INFO] 进入待机模式\n");
HAL_PWR_EnableWakeUpPin(LP_Entry_Cfg.WakeUpPin_Cfg.WakeUpPinPolarity);
delay_ms(10); //消抖
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
HAL_PWR_EnterSTANDBYMode();
break;
}
case 4:
{
printf("[INFO] 三秒后进入关机模式\n");
delay_ms(3000);
printf("[INFO] 进入关机模式\n");
HAL_PWR_EnableWakeUpPin(LP_Entry_Cfg.WakeUpPin_Cfg.WakeUpPinPolarity);
delay_ms(10); //消抖
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
HAL_PWREx_EnterSHUTDOWNMode();
break;
}
default:
{
printf("[INFO] 不进入低功耗模式\n");
break;
}
}
}
void Init_Enter_Low_PWR(SystemClock_Config_Callback SystemClock_Config_Fxn)
{
uint8_t i=0;
memset(&LP_Entry_Cfg,0,sizeof(LP_Entry_Cfg));
LP_Entry_Cfg.SystemClock_Config_Fxn=SystemClock_Config_Fxn;
LP_Entry_Cfg.SLEEPEntry_Cfg.SLEEPEntry=PWR_SLEEPENTRY_WFI;
LP_Entry_Cfg.STOPEntry_Cfg.STOPEntry=PWR_STOPENTRY_WFI;
LP_Entry_Cfg.RTC_Cfg.counter=RTC_WAKEUPCLOCK_RTCCLK_DIV16;
for(i=0;i<sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg)/sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg[0]);i++)
{
LP_Entry_Cfg.Device_Cfg.UART_Cfg[i].UART_WakeUpStruct.WakeUpEvent=UART_WAKEUP_ON_READDATA_NONEMPTY;
}
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/Mike_Zhou_Admin/STM32L4_HAL_User_LOW_POWER_Lib.git
git@gitee.com:Mike_Zhou_Admin/STM32L4_HAL_User_LOW_POWER_Lib.git
Mike_Zhou_Admin
STM32L4_HAL_User_LOW_POWER_Lib
基于HAL库建立自己的低功耗模式配置库(STM32L4系列低功耗所有配置汇总)
master

搜索帮助