当前位置: 首页 > news >正文

【STM32笔记】低功耗模式配置及避坑汇总

【STM32笔记】低功耗模式配置及配置汇总

文章总结:(后续更新以相关文章为准)
【STM32笔记】__WFI();进入不了休眠的可能原因

【STM32笔记】HAL库低功耗模式配置(ADC唤醒无法使用、低功耗模式无法烧录解决方案)

【STM32笔记】低功耗模式下GPIO省电配置避坑实验(闲置引脚配置为模拟输入其实更耗电)

【STM32笔记】低功耗模式下GPIO、外设、时钟省电配置避坑

【STM32笔记】低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER)

【STM32笔记】HAL库低功耗STOP停止模式的串口唤醒(解决进入以后立马唤醒、串口唤醒和回调无法一起使用的问题)

HAL库低功耗模式配置(ADC唤醒无法使用、低功耗模式无法烧录解决方案)

一、低功耗模式简介

系统提供了多个低功耗模式,可在 CPU 不需要运行时(例如等待外部事件时)节省功耗。由用户根据应用选择具体的低功耗模式,以在低功耗、短启动时间和可用唤醒源之间寻求最佳平衡。

睡眠模式、停止模式及待机模式中,若备份域电源正常供电,备份域内的 RTC 都可以正常运行,备份域内的寄存器的数据会被保存,不受功耗模式影响。

从表中可以看到,这三种低功耗模式层层递进,运行的时钟或芯片功能越来越少,因而功耗越来越低。

模式说明进入方式唤醒对1.8V区域时钟的影响对VDD区域时钟的影响调压器
睡眠模式内核停止,所有外设包括M3核心的外设,如NVIC、系统时钟(SysTick)等仍在运行WFI、WFE命令(HAL库直接调用)任意中断/事件内核时钟关,对其他时钟和ADC时钟无影响
停止模式所有的时钟都已停止配置PWR_CR寄存器的PDDS+LPDS位+SLEEPDEEP位+WFI或WFE命令任意外部中断EXTI(在外部中断寄存器中设置)关闭所有1.8V区域的时钟HSI和HSE的振荡器关闭开启或处于低功耗模式(依据电源控制寄存器的设定)
待机模式1.8V电源关闭配置PWR_CR寄存器的PDDS+SLEEPDEEP位+WFI或WFE命令WKUP、引脚的RTC闹钟事件、NRST引脚上的外部复位、IWDG复位关闭所有1.8V区域的时钟HSI和HSE的振荡器关闭

L4及L4+的通用模式状态表可见手册
在这里插入图片描述
在这里插入图片描述

blog.csdn.net/weixin_53403301/article/details/129031935
【STM32笔记】低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER)

blog.csdn.net/weixin_53403301/article/details/129055530
【STM32笔记】低功耗模式下GPIO省电配置避坑实验(闲置引脚配置为模拟输入其实更耗电)

blog.csdn.net/weixin_53403301/article/details/129060093
【STM32笔记】低功耗模式下GPIO、外设省电配置避坑

1.1 睡眠模式

在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,CM3 核心的外设全都还照常运行。有两种方式进入睡眠模式,它的进入方式决定了从睡眠唤醒的方式,分别是 WFI(wait for interrupt) 和 WFE(wait for event),即由等待“中断”唤醒和由“事件”唤醒。

特性和说明:立即睡眠: 在执行 WFI 或 WFE 指令时立即进入睡眠模式。
退出时睡眠: 在退出优先级最低的中断服务程序后才进入睡眠模式。
进入方式: 内核寄存器的 SLEEPDEEP=0 ,然后调用 WFI 或 WFE 指令即可进入睡眠模式;SLEEPONEXIT=1 时,进入“退出时睡眠”模式。
唤醒方式: 如果是使用 WFI 指令睡眠的,则可使用任意中断唤醒;如果是使用 WFE 指令睡眠的,则由事件唤醒。
睡眠时: 关闭内核时钟,内核停止,而外设正常运行,在软件上表现为不再执行新的代码。这个状态会保留睡眠前的内核寄存器、内存的数据。
唤醒延迟: 无延迟。
唤醒后: 若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。

唤醒后即可开始行动 继续程序 无需配置任何寄存器

睡眠模式和低功耗睡眠模式是两个模式 由PWR_MAINREGULATOR_ON和PWR_LOWPOWERREGULATOR_ON两个变量确定

要进入低功耗睡眠模式 首先得进入低功耗运行模式
HAL_PWREx_EableLowPowerRunMode()
且工作频率降低到2MHz以下

唤醒时 睡眠模式直接唤醒

而低功耗睡眠模式唤醒后 会进入到低功耗运行模式 若想正常工作 需用HAL_PWREx_DisableLowPowerRunMode()退出

1.2 停止模式

在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其 1.8V 区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。停止模式可以由任意一个外部中断(EXTI)唤醒,在停止模式中可以选择电压调节器为开模式或低功耗模式。

特性和说明:调压器低功耗模式: 在停止模式下调压器可工作在正常模式或低功耗模式,可进一步降低功耗。
进入方式: 内核寄存器的 SLEEPDEEP=1,PWR_CR 寄存器中的 PDDS=0,然后调用 WFI 或 WFE 指令即可进入停止模式;PWR_CR 寄存器的 LPDS=0 时,调压器工作在正常模式,LPDS=1 时工作在低功耗模式。
唤醒方式: 如果是使用 WFI 指令睡眠的,可使用任意 EXTI 线的中断唤醒;如果是使用 WFE 指令睡眠的,可使用任意配置为事件模式的 EXTI 线事件唤醒。
停止时: 内核停止,片上外设也停止。这个状态会保留停止前的内核寄存器、内存的数据。
唤醒延迟: 基础延迟为 HSI 振荡器的启动时间,若调压器工作在低功耗模式,还需要加上调压器从低功耗切换至正常模式下的时间。
唤醒后: 若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。唤醒后,STM32 会使用 HSI 作为系统时钟。

只能由外部中断唤醒 唤醒后需要重新使能时钟(SystemClock_Config();)
建议将一条外部中断线专门作为唤醒中断,执行中断后进入回调进行时钟使能

停止模式0和1由PWR_MAINREGULATOR_ON和PWR_LOWPOWERREGULATOR_ON两个变量确定

停止模式0和1可以被串口 I2C等设备唤醒(具体看手册)

停止模式2则在pwr_ex.c中进入

停止模式2 只能被特定器件(如LPUART等在内部与EXTI有链接的器件)唤醒

详情见后续关于STOP模式串口唤醒的文章

blog.csdn.net/weixin_53403301/article/details/129014963
【STM32笔记】HAL库低功耗STOP停止模式的串口唤醒(解决串口唤醒和回调无法一起使用的问题)

1.3 待机模式

翻译成shutdown更为合适
待机模式,它除了关闭所有的时钟,还把 1.8V 区域的电源也完全关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测 boot 条件,从头开始执行程序。它有四种唤醒方式,分别是 WKUP(PA0)引脚的上升沿,RTC 闹钟事件,NRST 引脚的复位和 IWDG(独立看门狗)复位。

特性和说明:进入方式: 内核寄存器的 SLEEPDEEP=1,PWR_CR 寄存器中的 PDDS=1,PWR_CR 寄存器中的唤醒状态位 WUF=0,然后调用 WFI 或 WFE 指令即可进入待机模式。
唤醒方式: 通过 WKUP ,RTC 闹钟、唤醒、入侵、时间戳事件或 NRST 引脚外部复位及 IWDG 复位唤醒。
待机时: 内核停止,片上外设也停止;内核寄存器、内存的数据会丢失;除复位引脚、RTC_AF1 引脚及 WKUP 引脚,其它 I/O 口均工作在高阻态。
唤醒延迟: 芯片复位的时间。
唤醒后: 相当于芯片复位,在程序表现为从头开始执行代码。

代码实操

SYS配置:
在这里插入图片描述
选择Serial Wire模式可以在某些情况下进行调试如(SWD)
在这里插入图片描述
配置外部中断就不说了

进入低功耗模式的函数

引脚指定只对待机模式有效
HAL_PWR_EnableWakeUpPin(WakeUpPinPolarity);
该函数指定的引脚不受引脚其他的配置影响(比如已经被配成了复用)

__HAL_RCC_PWR_CLK_ENABLE(); 可要可不要

/*!* @brief       	进入低功耗模式   	** @param 	[in]	mode_flag: 模式标志* 								0/大于4 不进入任何模式,1 进入睡眠,2 进入停止,3 进入待机,4 关机*  				[in]	WakeUpPinPolarity: 待机模式下WKUP唤醒引脚极性配置,其他模式无用** @return				None*/
void Enter_Low_PWR(uint8_t mode_flag,uint32_t WakeUpPinPolarity)
{__HAL_RCC_PWR_CLK_ENABLE();switch(mode_flag){case 0:{printf("[INFO] 不进入低功耗模式\n");break;}case 1:{printf("[INFO] 进入睡眠模式\n");delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);	HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFI);break;}case 2:{printf("[INFO] 进入停止模式\n");delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_SLEEPENTRY_WFI);break;}case 3:{printf("[INFO] 三秒后进入待机模式\n");delay_ms(3000);printf("[INFO] 进入待机模式\n");HAL_PWR_EnableWakeUpPin(WakeUpPinPolarity);delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);HAL_PWR_EnterSTANDBYMode();break;}case 4:{printf("[INFO] 三秒后进入关机模式\n");delay_ms(3000);printf("[INFO] 进入关机模式\n");HAL_PWR_EnableWakeUpPin(WakeUpPinPolarity);delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);HAL_PWREx_EnterSHUTDOWNMode();break;}default:{printf("[INFO] 不进入低功耗模式\n");break;}}
}

外部中断回调(只对STOP模式有效果 SLEEP模式可以省略):

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{switch(GPIO_Pin){case WAKE_UP_Pin:{__HAL_RCC_PWR_CLK_ENABLE();SystemClock_Config();}default:{			break;}}__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
}

ADC唤醒无法使用的解决方案

从STOP模式唤醒 会导致L4系列等MCU的ADC无法使用
原因就是PLLSAI时钟被关闭了 就算从头开始初始化也没用
所以只需要更换时钟源即可
在这里插入图片描述

低功耗模式无法烧录解决方案

在进入到待机模式后 相当于强行死机了 无法进行烧录 只能手动复位后烧录
但在调试时 将进入待机模式的函数放得太前 则完全无法烧录 这时候就需要从硬件方面解决

把烧写程序用的软件复位NREST引脚与硬件复位短接 那么在烧写时 复位引脚就会被拉低 这样的话 就算是shutdown模式 也能正常烧写程序 就不用像之前那样一直按复位卡时间点了

省电优化

在CubeMX里面有一项设置,就是把没有用到的引脚全部设置为省电(模拟输入) 这样可以更省电
在这里插入图片描述

HAL库低功耗STOP停止模式的串口唤醒(解决进入以后立马唤醒、串口唤醒和回调无法一起使用的问题)

低功耗模式如图所示

停止模式有三种 分别是0 1 2
其中 0 1可以由串口唤醒
2只能由LPUART唤醒
在这里插入图片描述
在这里插入图片描述
在手册里可以查到

进入也很简单:

/*!* @brief       	进入低功耗模式   	** @param 	[in]	mode_flag: 模式标志* 								0/大于4 不进入任何模式,1 进入睡眠,2 进入停止,3 进入待机,4 关机*  				[in]	WakeUpPinPolarity: 待机模式下WKUP唤醒引脚极性配置,其他模式无用** @return				None*/
void Enter_Low_PWR(uint8_t mode_flag,uint32_t WakeUpPinPolarity)
{	__HAL_RCC_PWR_CLK_ENABLE();switch(mode_flag){case 0:{printf("[INFO] 不进入低功耗模式\n");break;}case 1:{printf("[INFO] 进入睡眠模式\n");delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);	HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFI);break;}case 2:{printf("[INFO] 进入停止模式\n");delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_SLEEPENTRY_WFI);break;}case 3:{printf("[INFO] 三秒后进入待机模式\n");delay_ms(3000);printf("[INFO] 进入待机模式\n");HAL_PWR_EnableWakeUpPin(WakeUpPinPolarity);delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);HAL_PWR_EnterSTANDBYMode();break;}case 4:{printf("[INFO] 三秒后进入关机模式\n");delay_ms(3000);printf("[INFO] 进入关机模式\n");HAL_PWR_EnableWakeUpPin(WakeUpPinPolarity);delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);HAL_PWREx_EnterSHUTDOWNMode();break;}default:{printf("[INFO] 不进入低功耗模式\n");break;}}
}

要进入停止2模式则需要在pwr_ex.c中配置
HAL_PWREx_EnterSTOP2Mode();函数

其中
HAL_PWR_EnterSTOPMode中的PWR_MAINREGULATOR_ON、PWR_LOWPOWERREGULATOR_ON分别是开启稳压器和关闭稳压器 分别对应STOP 0和1

在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其 1.8V 区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。停止模式可以由任意一个外部中断(EXTI)唤醒,在停止模式中可以选择电压调节器为开模式或低功耗模式。

特性和说明:调压器低功耗模式: 在停止模式下调压器可工作在正常模式或低功耗模式,可进一步降低功耗。
进入方式: 内核寄存器的 SLEEPDEEP=1,PWR_CR 寄存器中的 PDDS=0,然后调用 WFI 或 WFE 指令即可进入停止模式;PWR_CR 寄存器的 LPDS=0 时,调压器工作在正常模式,LPDS=1 时工作在低功耗模式。
唤醒方式: 如果是使用 WFI 指令睡眠的,可使用任意 EXTI 线的中断唤醒;如果是使用 WFE 指令睡眠的,可使用任意配置为事件模式的 EXTI 线事件唤醒。
停止时: 内核停止,片上外设也停止。这个状态会保留停止前的内核寄存器、内存的数据。
唤醒延迟: 基础延迟为 HSI 振荡器的启动时间,若调压器工作在低功耗模式,还需要加上调压器从低功耗切换至正常模式下的时间。
唤醒后: 若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。唤醒后,STM32 会使用 HSI 作为系统时钟。

只能由外部中断唤醒 唤醒后需要重新使能时钟(SystemClock_Config();)
建议将一条外部中断线专门作为唤醒中断,执行中断后进入回调进行时钟使能

停止模式0和1由PWR_MAINREGULATOR_ON和PWR_LOWPOWERREGULATOR_ON两个变量确定

停止模式0和1可以被串口 I2C等设备唤醒(具体看手册)

停止模式2则在pwr_ex.c中进入

停止模式2 只能被特定器件(如LPUART等在内部与EXTI有链接的器件)唤醒

若要配置UART唤醒 则需要:

/*!* @brief       	配置串口在停止模式下的唤醒   	** @param 	[in]	huart: UART_HandleTypeDef类型的器件*					[in]	EnableNotDisable: 使能或者关闭** @return				None*/
void Ctrl_UART_StopMode_WakeUp(UART_HandleTypeDef *huart,bool EnableNotDisable)
{	if(EnableNotDisable){__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI);	//保留唤醒用的HSI线 串口初始化时钟也必须要配置为HSIUART_WakeUpTypeDef UART_WakeUpStruct={0};UART_WakeUpStruct.WakeUpEvent=UART_WAKEUP_ON_READDATA_NONEMPTY;  //接收数据不为空时唤醒HAL_UARTEx_StopModeWakeUpSourceConfig(huart,UART_WakeUpStruct);__HAL_UART_ENABLE_IT(&huart2,UART_IT_WUF);	//开启唤醒中断HAL_UARTEx_EnableStopMode(huart);		//开启模式}else{__HAL_UART_DISABLE_IT(&huart2,UART_IT_WUF);	//关闭唤醒中断HAL_UARTEx_DisableStopMode(huart);		//关闭模式}
}

配置为接收数据就唤醒

若要使用 则UART必须为HSI或MSI时钟 配置太麻烦 所以我建议直接在HAL里面配置
在这里插入图片描述
串口回调一般是:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart==&huart2){		HAL_UART_Transmit(&huart2,&RxBuffer,1,0xFFFF);HAL_UART_Receive_IT(&huart2,&RxBuffer,1);}if(huart==&huart4){		HAL_UART_Transmit(&huart4,&RxBuffer,1,0xFFFF);HAL_UART_Receive_IT(&huart4,&RxBuffer,1);}
}

接收数据后发送数据
而唤醒回调则是:

void HAL_UARTEx_WakeupCallback(UART_HandleTypeDef *huart)
{if(huart==&huart2){		__HAL_RCC_PWR_CLK_ENABLE();SystemClock_Config();		Ctrl_UART_StopMode_WakeUp(huart,false);}
}

进入以后立马唤醒

若是串口悬空 或硬件设计问题 串口数据不定 则可能进入以后立马被唤醒
在这里插入图片描述
在外部硬件上加下拉 或者软件配置下拉(或上拉)即可 不过下拉更省电

串口唤醒和回调无法一起使用的问题

在调试时 发现串口唤醒和回调无法一起使用 进入了回调以后就退出了 不会进入串口唤醒

其实就是

__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI);	//保留唤醒用的HSI线 串口初始化时钟也必须要配置为HSI

的问题

若不使用这个语句 虽然串口可以用 也能接收数据并返回 但是进不了唤醒回调

其实就是因为时序被改变了

进入低功耗以后 接收数据唤醒 则先进入接收回调 然后发一次数据 但不会进行唤醒 因为时序有问题 mcu认为接收的数据不正常

保留这个语句以后就好了

另外 每次进入低功耗前 都要先调用

Ctrl_UART_StopMode_WakeUp(huart,true);

语句 否则无法正常唤醒 也不能再次进入低功耗模式

为了避免出错 每次唤醒以后都应该清空调唤醒中断

STOP模式会关闭时钟 所以建议是回调以后就初始化时钟一次

Ctrl_UART_StopMode_WakeUp(&huart2,true);
Enter_Low_PWR(2,0);

LPUART的配置同理 完全一模一样的语句

低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER)

低功耗模式如图所示

停止模式有三种 分别是0 1 2
其中 0 1可以由串口唤醒
2只能由LPUART唤醒
在这里插入图片描述
在这里插入图片描述
在手册里可以查到

进入也很简单:

/*!* @brief       	进入低功耗模式   	** @param 	[in]	mode_flag: 模式标志* 								0/大于4 不进入任何模式,1 进入睡眠,2 进入停止,3 进入待机,4 关机*  				[in]	WakeUpPinPolarity: 待机模式下WKUP唤醒引脚极性配置,其他模式无用** @return				None*/
void Enter_Low_PWR(uint8_t mode_flag,uint32_t WakeUpPinPolarity)
{	__HAL_RCC_PWR_CLK_ENABLE();switch(mode_flag){case 0:{printf("[INFO] 不进入低功耗模式\n");break;}case 1:{printf("[INFO] 进入睡眠模式\n");delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);	HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFI);break;}case 2:{printf("[INFO] 进入停止模式\n");delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_SLEEPENTRY_WFI);break;}case 3:{printf("[INFO] 三秒后进入待机模式\n");delay_ms(3000);printf("[INFO] 进入待机模式\n");HAL_PWR_EnableWakeUpPin(WakeUpPinPolarity);delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);HAL_PWR_EnterSTANDBYMode();break;}case 4:{printf("[INFO] 三秒后进入关机模式\n");delay_ms(3000);printf("[INFO] 进入关机模式\n");HAL_PWR_EnableWakeUpPin(WakeUpPinPolarity);delay_ms(10);  //消抖__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);HAL_PWREx_EnterSHUTDOWNMode();break;}default:{printf("[INFO] 不进入低功耗模式\n");break;}}
}

要进入停止2模式则需要在pwr_ex.c中配置
HAL_PWREx_EnterSTOP2Mode();函数

其中
HAL_PWR_EnterSTOPMode中的PWR_MAINREGULATOR_ON、PWR_LOWPOWERREGULATOR_ON分别是开启稳压器和关闭稳压器 分别对应STOP 0和1

所有的模式都可以用RTC唤醒
在这里插入图片描述
在这里插入图片描述
可以在手册里面找到外部中断线 所以RTC可以唤醒任一模式
在这里插入图片描述
RTC开启Internal WakeUp

在Wake Up配置中 完全不用设置(后面的语句可以直接配)
另外开启唤醒中断
在这里插入图片描述
在进入低功耗前 需要先调用RTC唤醒中断配置

HAL_RTCEx_SetWakeUpTimer_IT(&hrtc,counter,clock);

同样 退出时 要在注册的回调里面关闭中断

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{__HAL_RCC_PWR_CLK_ENABLE();SystemClock_Config();__HAL_RTC_WAKEUPTIMER_EXTI_DISABLE_IT();	
}

打包一下就是:

/*!* @brief       	配置RTC在低功耗模式下的唤醒   	** @param 	[in]	counter: 计数值*					[in]	clock: 时钟值*					[in]	EnableNotDisable: 使能或者关闭** @return				None*/
void Ctrl_RTC_WakeUp(uint32_t counter,uint32_t clock,bool EnableNotDisable)
{	if(EnableNotDisable){HAL_RTCEx_SetWakeUpTimer_IT(&hrtc,counter,clock);}else{__HAL_RTC_WAKEUPTIMER_EXTI_DISABLE_IT();	}
}

调用:

	Ctrl_UART_StopMode_WakeUp(&huart2,true);Ctrl_RTC_WakeUp(2000,RTC_WAKEUPCLOCK_RTCCLK_DIV16,true);Enter_Low_PWR(2,0);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){printf("[INFO] ADC: %f\n",Get_Real_ADC_Value(&hadc2));Count_ADXL345();Count_TMP75();TIM_Delay_ms(1000,&htim6);Ctrl_UART_StopMode_WakeUp(&huart2,true);Ctrl_RTC_WakeUp(2000,RTC_WAKEUPCLOCK_RTCCLK_DIV16,true);Enter_Low_PWR(2,0);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}

低功耗模式下GPIO、外设、时钟省电配置避坑

省电外设配置

在进入低功耗模式前 可以通过降低时钟频率 关闭GPIO口(通常是配置为模拟输入 或降低GPIO时钟) 以及关闭不需要的外设来降低功耗(尤其是在SLEEP、STOP模式)

时钟的话 如果用不上 或者进入STOP等模式 则也不需要配置 进入模式时 时钟本身就会被配置

关闭外设的函数在进入低功耗的前一步执行 相关唤醒配置需要在关闭外设前执行 且要注意 不能关闭用于唤醒的外设及其GPIO口

而用不到的外设和对应的GPIO口 建议同时关闭

以我的为例:

/*!* @brief       	所有外设初始化配置,根据使用需求来写** @param 	[in]	EnableNotDisable: 使能或者关闭*								true: 进行初始化外设(不包含时钟初始化)*								false: 或者关闭所有外设,所有GPIO配置为无上拉下拉且模拟输入,仅保留系统时钟和系统所需的GPIO口复用*								该函数在进入低功耗前调用(false)*								建议在进入该函数前(false)先配置用于唤醒的外设 如指定UART或RTC作为唤醒使用 然后再调用该函数 且不能关闭有唤醒功能的外设*								若用于唤醒后的初始化,则建议先初始化时钟,再执行该函数的初始化(true)*								在休眠期间使用的外设,不要关闭,也不要关闭GPIO等;相反,外设和GPIO等建议同时关闭(避免出现bug,并且也省电)*								未关闭,但唤醒时重复初始化外设并不受影响*								若未关闭的外设在运行中改变了初始化值,则建议不在唤醒时运行该初始化(前提是外设的GPIO等也没有作改动)*								若需要在初始化后更改初始化值,则建议要么不进行初始化且不关闭(也包括GPIO等),或重新设置新值** @return				None*/
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配置为复用}
}

true: 进行初始化外设(不包含时钟初始化)
false: 或者关闭所有外设,所有GPIO配置为无上拉下拉且模拟输入,仅保留系统时钟和系统所需的GPIO口复用
该函数在进入低功耗前调用(false)
建议在进入该函数前(false)先配置用于唤醒的外设 如指定UART或RTC作为唤醒使用 然后再调用该函数 且不能关闭有唤醒功能的外设
若用于唤醒后的初始化,则建议先初始化时钟,再执行该函数的初始化(true)
在休眠期间使用的外设,不要关闭,也不要关闭GPIO等;相反,外设和GPIO等建议同时关闭(避免出现bug,并且也省电)
未关闭,但唤醒时重复初始化外设并不受影响
若未关闭的外设在运行中改变了初始化值,则建议不在唤醒时运行该初始化(前提是外设的GPIO等也没有作改动)
若需要在初始化后更改初始化值,则建议要么不进行初始化且不关闭(也包括GPIO等),或重新设置新值

GPIO配置也是个坑
(相关配置及唤醒功能等 见前文)

/*!* @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*/
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 PC2PC3 PC4 PC5 PC6PC7 PC8 PC9 PC10PC11 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 PA3PA4 PA5 PA6 PA7PA8 PA9 PA10 PA11PA12 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 PB10PB11 PB12 PB13 PB14PB15 PB3 PB4 PB5PB6 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);}
}

注意:用于串口唤醒等的引脚,不可配置为模拟输入,也不可关闭
在进行GPIO初始化前,先将GPIO_DeInit,但是不做也不影响,不过还是建议跑一下
以优先级顺序来看:
如果这一组GPIO都没用到过 那么直接不开启时钟就最省电
如果这一组GPIO有引脚用过了 时钟不能关 那么就将用过的引脚配置为模拟输入
切记!!!:
不要将没用过的引脚配置为模拟输入 耗电量其实会稍微增加一点!
不要将没用过的GPIO时钟打开以后再配置为模拟输入 耗电量会增加很多 就算配置后再关时钟也没用!
尽量不要勾选CubeMX中的配置闲置引脚为模拟输入的选项 没用到的时钟还开启了会增加很多耗电
低功耗模式配置:
在进入STOP模式时 GPIO会保留原本的状态 所以把开启后不需要再保留的GPIO配置为模拟输入确实省电 时钟的话不用的肯定关 其他的反正都会关(除了保留的时钟)
在进入SLEEP模式时 时钟并不会关闭 所以时钟应手动关闭 且将开启后的GPIO配置为模拟输入
待机模式和关机模式就更不用在意GPIO口耗电了

而低功耗进入则改成了:
(相关配置及唤醒功能等 见前文)

printf("[INFO] 进入停止模式\n");
delay_ms(10);  //消抖
PWR_Device_Init(false);			
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_SLEEPENTRY_WFI);
break;

同样 在唤醒后 唤醒回调里面也得先配置时钟再进行外设初始化

void HAL_UARTEx_WakeupCallback(UART_HandleTypeDef *huart)
{if(huart==&huart2){		__HAL_RCC_PWR_CLK_ENABLE();HAL_Init();SystemClock_Config();		Ctrl_UART_StopMode_WakeUp(huart,false);PWR_Device_Init(true);}
}void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{__HAL_RCC_PWR_CLK_ENABLE();HAL_Init();SystemClock_Config();Ctrl_RTC_WakeUp(0,0,false);PWR_Device_Init(true);
}

不过 HAL_Init可以省略 但是为了避免出现bug 还是放在这里.

调用的时候:

	Ctrl_UART_StopMode_WakeUp(&huart2,true);Ctrl_RTC_WakeUp(20000,RTC_WAKEUPCLOCK_RTCCLK_DIV16,true);Enter_Low_PWR(2,0);

先配置唤醒用的功能 再进入低功耗

当然 唤醒用的外设也需要在最开始进行初始化配置

GPIO省电模式实验

众所周知 GPIO配置为模拟输入最省电

在CubeMX中 有一项可以将未用到的引脚全部配置为模拟输入

在这里插入图片描述
若是不开启任何GPIO 以STM32L496RGT6为例 函数为:

/*** @brief GPIO Initialization Function* @param None* @retval None*/
static void MX_GPIO_Init(void)
{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 PC2PC3 PC4 PC5 PC6PC7 PC8 PC9 PC10PC11 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 PA3PA4 PA5 PA6 PA7PA8 PA9 PA10 PA11PA12 PA15 */GPIO_InitStruct.Pin = 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_PIN_15;GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/*Configure GPIO pins : PB0 PB1 PB2 PB10PB11 PB12 PB13 PB14PB15 PB3 PB4 PB5PB6 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);}

可以看到 在配置之前 需要先开启GPIO时钟

另外 如果配置了外部晶振或系统调试 则仅会开启时钟 而不会有任何引脚复用的配置

  __HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOH_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();

但无论是官方手册 还是官方例程 引脚配置前都应开启时钟 再进行配置
所以在进行模拟输入配置时 都会把所有的引脚时钟开启

以我做的最小系统板进行测试:
download.csdn.net/download/weixin_53403301/86930297

在这里插入图片描述
其主要耗电为两个LED灯
去掉这两个LED灯 并且进入待机模式后 实测只有8uA以下的功耗

这两个LED灯也是为了方便测试

在恒压源3.3V供电的前提下 从以下四个方面进行测试 每次都在电流稳定后读取 三次复位以后取平均值 精度10uA:

  1. 按CubeMX生成的所有闲置引脚模拟输入代码 直接烧录进去
  2. 按CubeMX生成的代码 去掉GPIOB和GPIOD的时钟(仅保留系统调试和高低速外部晶振) 再进行初始化
  3. 按CubeMX生成的代码 进行初始化以后 再去掉GPIOB和GPIOD的时钟(仅保留系统调试和高低速外部晶振)
  4. 不进行GPIO初始化 GPIO时钟仅保留系统调试和高低速外部晶振时钟 关闭GPIOB和GPIOD的时钟
  5. 只开启GPIOC、GPIOA、GPIOH的时钟,并且将C、A、H剩下的引脚配置为模拟输入 关闭GPIOB和GPIOD的时钟且不对其进行任何配置

其实测电流如下:

12345
按CubeMX生成的所有闲置引脚模拟输入代码 直接烧录进去按CubeMX生成的代码 去掉GPIOB和GPIOD的时钟(仅保留系统调试和高低速外部晶振) 再进行初始化按CubeMX生成的代码 进行初始化以后 再去掉GPIOB和GPIOD的时钟(仅保留系统调试和高低速外部晶振)不进行GPIO初始化 GPIO时钟仅保留系统调试和高低速外部晶振时钟 关闭GPIOB和GPIOD的时钟只开启GPIOC、GPIOA、GPIOH的时钟,并且将C、A、H剩下的引脚配置为模拟输入 关闭GPIOB和GPIOD的时钟且不对其进行任何配置
1.27mA1.12mA1.29mA1.08mA1.13mA(同2)

其中 功耗最低的是不进行模拟输入初始化的方案 仅关闭不需要的时钟 其次是仅配置需要保留的GPIO组时钟 并把其他引脚配置为模拟输入

在关闭时钟时 GPIO配置其实是无效的(还是会进行配置 将相关参数写入寄存器 但GPIO不会工作 所以无效)

所以方案2和方案5等价的 这两者都是在初始化之前没开启时钟 方案2虽然将寄存器写入 但功耗其实和5差不多 实验也存在误差 所以可以忽略不计

而方案4则更加直接了 没有进行任何GPIO配置 直接关闭不用的时钟

重点是1和3
1的话就是开启了所有时钟 然后把闲置引脚都配置为模拟输入
3则是进行1之后 再关闭闲置时钟
其中 3的耗电量是最高的 尽管初始化后面关闭了时钟 但其实最多就是跟1差不多 完全比不上2 4 5 甚至功耗比1还高(虽然可能存在误差)

所以 综合来说 以省电优先级顺序来看:
如果这一组GPIO都没用到过 那么直接不开启时钟就最省电
如果这一组GPIO有引脚用过了 时钟不能关 那么就将用过的引脚配置为模拟输入
尽量不要勾选CubeMX中的配置闲置引脚为模拟输入的选项 没用到的时钟还开启了会增加很多耗电

切记!!!:
不要将没用过的引脚配置为模拟输入 耗电量其实会稍微增加一点!
不要将没用过的GPIO时钟打开以后再配置为模拟输入 耗电量会增加很多 就算配置后再关时钟也没用!

低功耗模式扩展

在进入STOP模式时 GPIO会保留原本的状态 所以把开启后不需要再保留的GPIO配置为模拟输入确实省电 时钟的话不用的肯定关 其他的反正都会关(除了保留的时钟)

在进入SLEEP模式时 时钟并不会关闭 所以时钟应手动关闭 且将开启后的GPIO配置为模拟输入

待机模式和关机模式就更不用在意GPIO口耗电了

附上我进入低功耗前的GPIO配置函数:

/*!* @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*/
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 PC2PC3 PC4 PC5 PC6PC7 PC8 PC9 PC10PC11 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 PA3PA4 PA5 PA6 PA7PA8 PA9 PA10 PA11PA12 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 PB10PB11 PB12 PB13 PB14PB15 PB3 PB4 PB5PB6 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);}
}

__WFI();进入不了休眠的可能原因

__WFI();为汇编指令

/**\brief   Wait For Interrupt\details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs.*/
#define __WFI                             __wfi

其作用就是设备休眠 并等待任意中断实践唤醒
在这里插入图片描述

实际调用:

	__WFI();

但是 基本上直接都执行不了

最常见的就是中断没清理掉

在这里插入图片描述
在Keil的调试中可以看到活跃的中断
在这里插入图片描述
EPA分别表示Enable Pending Active

前两个表示开启但未发生 Active表示正在发生

所以进入休眠前需要调用中断清理
如:

__disable_irq(); 

或:

__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);	  //清理唤醒标志 防止立刻唤醒

等标志

另外 实测发现 即使没有中断或唤醒标志 也会导致__WFI();无法进入 特别是程序刚开始运行的时候 这里其实就是没消抖 需要延时一会(哪怕1us)

	delay_us(1);__WFI();

再者 在进行调试时 如果采用单步调试(也相当于一种中断) 则会执行__WFI();后立马执行下一句

所以可以在__WFI();之前和之后打一个断点 用全速跑来判断是否进入

相关文章:

【STM32笔记】低功耗模式配置及避坑汇总

【STM32笔记】低功耗模式配置及配置汇总 文章总结:(后续更新以相关文章为准) 【STM32笔记】__WFI();进入不了休眠的可能原因 【STM32笔记】HAL库低功耗模式配置(ADC唤醒无法使用、低功耗模式无法烧录解决方案&#x…...

DFN: Dynamic Filter Networks-动态卷积网络

一、论文信息 论文名称:Dynamic Filter Networks 作者团队:NIPS2016 二、动机与创新 卷积层是通过将上一层的特征映射与一组过滤器进行卷积计算输出特征映射,滤波器是卷积层的唯一参数,通常用反向传播算法在训练中学习&#xff…...

面试官:你是怎样理解Fiber的

hello,这里是潇晨,今天我们来聊一聊Fiber。不知道大家面试的时候有没有遇到过和react Fiber相关的问题呢,这一类问题比较开放,但也是考察对react源码理解深度的问题,如果面试高级前端岗,恰巧你平时用的是re…...

【C++的OpenCV】第一课-opencv的介绍和安装(Linux环境下)

第一课-目录一、基本介绍1.1 官网1.2 git源码1.3 介绍二、OpenCV的相关部署工作2.1 Linux平台下部署OpenCV一、基本介绍 1.1 官网 opencv官网 注意:官网为英文版本,可以使用浏览器自带的翻译插件进行翻译,真心不推荐大家去看别人翻译的&am…...

k8s安装tekton,编写task

文章目录一、官方安装二、国内资源安装安装tekton安装dashboard安装CLI三、demo编写task.yaml编写taskRun.yaml使用tkn命令查看参考文章一、官方安装 地址:https://tekton.dev/docs/installation/pipelines/#installing-tekton-pipelines-on-kubernetes 注意&#…...

K_A12_014 基于STM32等单片机驱动S12SD紫外线传感器模块 串口与OLED0.96双显示

K_A12_014 基于STM32等单片机驱动S12SD紫外线传感器模块 串口与OLED0.96双显示一、资源说明二、基本参数参数引脚说明三、驱动说明IIC地址/采集通道选择/时序对应程序:数据对比:四、部分代码说明1、接线引脚定义1.1、STC89C52RCS12SD紫外线传感器模块1.2、STM32F103…...

还真不错,今天 Chatgpt 教会我如何开发一款小工具开发(Python 代码实现)

上次使用 Chatgpt 写爬虫,虽然写出来的代码很多需要修改后才能运行,但Chatgpt提供的思路和框架都是没问题。 这次让 Chatgpt 写一写GUI程序,也就是你常看到的桌面图形程序。 由于第一次测试,就来个简单点的,用Python…...

Boom 3D最新版本下载电脑音频增强应用工具

为了更好地感受音乐的魅力,Boom 3D 可以让你对音效进行个性化增强,并集成 3D 环绕立体声效果,可以让你在使用任何耳机时,都拥有纯正、优质的音乐体验。Boom 3D是一款充满神奇魅力的3D环绕音效升级版,BOOM 3D是一个全新…...

redis-如何保证数据库和缓存双写一致性?

前言 数据库和缓存(比如:redis)双写数据一致性问题,是一个跟开发语言无关的公共问题。尤其在高并发的场景下,这个问题变得更加严重。 我很负责的告诉大家,该问题无论在面试,还是工作中遇到的概率…...

系列二、核心概念运行流程

一、镜像&容器&仓库 1.1、镜像 定义:一个镜像代表着一个软件,例如:mysql镜像、redis镜像、nginx镜像。 特点:只读 1.2、容器 定义:基于某个镜像运行一次就会生成一个程序实例,一个程序实例称之为一…...

恢复 iPhone 和 iPad 数据的 10 种简单工具

它发生了.. 有时您需要从您的手机或平板设备恢复重要数据。 许多人已经开始将重要文件存储在手机上,因为他们几乎可以在任何情况下随时随地轻松访问数据。 从技术上讲,您会在几分之一秒内丢失所有存储的信息、照片、视频、音乐、文档等。因此&#xff…...

经理与员工工资关系-课后程序(JAVA基础案例教程-黑马程序员编著-第四章-课后作业)

【案例4-6】经理与员工工资案例(利用多态实现) 欢迎点赞关注收藏 【案例介绍】 案例描述 某公司的人员分为员工和经理两种,但经理也属于员工中的一种,公司的人员都有自己的姓名和地址,员工和经理都有自己的工号、工…...

Micropython ESP32配置与烧录版本

下载ESP32的Micropython固件 官方连接https://www.micropython.org/download/esp32/ 看了下描述,上面的是IDF4.x系列编译,下面是IDF3.x系列编译,我们默认选新的 下载安装CP2102驱动 CP210x USB to UART Bridge VCP Drivers - Silicon Labs…...

java面试题-并发关键字(Synchronized,volatile,final)

Synchronized1.Synchronized可以作用在哪里?Synchronized可以作用在方法、代码块、静态方法和类上。方法public synchronized void method(){//同步代码块 }代码块Object lock new Object(); synchronized(lock){//同步代码块 }静态方法public static synchronized void stat…...

【笔试强训】Day_02

目录 一、选择题 1、 2、 3、 4、 5、 6、 7、 8、 9、 10、 二、编程题 1、排序子序列 2、倒置字符串 一、选择题 1、 使用printf函数打印一个double类型的数据,要求:输出为10进制,输出左对齐30个字符,4位精度。…...

DepGraph:适用任何结构的剪枝

文章目录摘要1、简介2、相关工作3、方法3.1、神经网络中的依赖关系3.2、依赖关系图3.3、使用依赖图剪枝4、实验4.1、设置。4.2、CIFAR的结果4.3、消融实验4.4、适用任何结构剪枝5、结论摘要 论文链接:https://arxiv.org/abs/2301.12900 源码:https://gi…...

【结构体版】通讯录

👦个人主页:Weraphael ✍🏻作者简介:目前是C语言学习者 ✈️专栏:项目 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点赞&#x…...

Debezium系列之:基于debezium采集数据到kafka,再从kafka将数据流式传输到下游数据库

Debezium系列之:基于debezium采集数据到kafka,再从kafka将数据流式传输到下游数据库 一、需求背景二、准备Debezium集群和相关jar包的详细步骤三、查看插件是否加载成功四、源数据库表结构五、根据源数据库表结构准备目标数据库的表六、基于debezium采集数据到kafka七、查看c…...

【2023】华为OD机试真题Java-题目0217-上班之路

上班之路 题目描述 Jungle生活在美丽的蓝鲸城,大马路都是方方正正,但是每天马路的封闭情况都不一样。 地图由以下元素组成: . — 空地,可以达到;* — 路障,不可达到;S — Jungle的家;T — 公司. 其中我们会限制Jungle拐弯的次数,同时Jungle可以清除给定个数的路障,现在…...

基于spring生态的基础后端开发及渗透测试流程(二)

基于spring生态的基础后端开发及渗透测试流程(二)安全设备IDS蜜罐安全加固渗透测试信息收集子域名域名注册信息企业信息端口扫描源码泄露路径扫描真实ip探测js扫描设备检测蜜罐识别waf识别社工爆破漏洞扫描系统扫描web扫描应急响应继上次写了一份基于spr…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...

自然语言处理——文本分类

文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益&#xff08;IG&#xff09; 分类器设计贝叶斯理论&#xff1a;线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别&#xff0c; 有单标签多类别文本分类和多…...