【极海APM32替代笔记】低功耗模式配置及配置汇总

文章总结:(后续更新以相关文章为准)
【STM32笔记】低功耗模式、WFI命令等进入不了休眠的可能原因(系统定时器SysTick一直产生中断)

【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的时钟且不对其进行任何配置

其实测电流如下:

1 2 3 4 5
按CubeMX生成的所有闲置引脚模拟输入代码 直接烧录进去 按CubeMX生成的代码 去掉GPIOB和GPIOD的时钟(仅保留系统调试和高低速外部晶振) 再进行初始化 按CubeMX生成的代码 进行初始化以后 再去掉GPIOB和GPIOD的时钟(仅保留系统调试和高低速外部晶振) 不进行GPIO初始化 GPIO时钟仅保留系统调试和高低速外部晶振时钟 关闭GPIOB和GPIOD的时钟 只开启GPIOC、GPIOA、GPIOH的时钟,并且将C、A、H剩下的引脚配置为模拟输入 关闭GPIOB和GPIOD的时钟且不对其进行任何配置
1.27mA 1.12mA 1.29mA 1.08mA 1.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();之前和之后打一个断点 用全速跑来判断是否进入

【极海APM32替代笔记】低功耗模式配置及配置汇总相关推荐

  1. 【极海APM32替代笔记】低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER)

    [极海APM32替代笔记]低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER) [STM32笔记]低功耗模式配置及避坑汇总 前文: blog.csdn.net/weixin_ ...

  2. 国鼎代理极海APM32系列MCU智能扫地机方案

    为智能家居新概念的领跑者,扫地机器人正站在"懒人经济"的风口,备受市场追捧.据奥维云网数据预计,2022年国内扫地机市场规模将达178亿. APM32智能扫地机应用方案 作为国产M ...

  3. stm32f103 低功耗调试笔记 低功耗模式下一直有个800多uA的电流

    最新在做一个低功耗项目 ,用到stm32f103芯片,用到的stop模式和standby模式. 因 stop 只比 standby 模式多1-3uA,最终选用了 stop 模式.下面是官方文档中对几种 ...

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

    [STM32笔记]低功耗模式配置及配置汇总 文章总结:(后续更新以相关文章为准) [STM32笔记]低功耗模式.WFI命令等进入不了休眠的可能原因(系统定时器SysTick一直产生中断) [STM32 ...

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

    [STM32笔记]HAL库低功耗模式配置(ADC唤醒无法使用.低功耗模式无法烧录解决方案) [STM32笔记]低功耗模式配置及避坑汇总 一.低功耗模式简介 系统提供了多个低功耗模式,可在 CPU 不需 ...

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

    [STM32笔记]低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER) [STM32笔记]低功耗模式配置及避坑汇总 前文: blog.csdn.net/weixin_5340 ...

  7. MCU极海 APM32F103VET6 兼容替代 STM32F103VET6

    MCU极海 APM32F103VET6 兼容替代 STM32F103VET6 APM32F103VET6产品概述 APM32F103xDxE系列MCU,基于32位Arm® Cortex®-M3内核,最 ...

  8. STM32低功耗模式下GPIO如何配置最节能?

    目录 1.将未使用的GPIO 配置为模拟输入模式 2.调节GPIO 速度 3.GPIO不使用时禁用寄存器时钟 4.避免悬空未使用的引脚 STM32低功耗模式下GPIO如何配置最节能,这里总结了一下四点 ...

  9. MSP430F5529 DriverLib 库函数学习笔记(十三)认识低功耗模式

    目录 硬知识 低功耗模式 MSP430单片机各工作模式下的电流消耗 API 进入低功耗模式 退出低功耗模式 平台:Code Composer Studio 10.3.1 MSP430F5529 Lau ...

最新文章

  1. 绝对实用 NAT + VLAN +ACL管理企业网络
  2. 【Todo】Java的JIT机制
  3. 详述@ResponseBody和@RequestBody注解的区别
  4. Create a Service Metrics Strategy
  5. i2s传输中左右声道数据格式
  6. 如何在Linux下写无线网卡的驱动
  7. PHP生成Mysql数据字典
  8. Linux awk 命令
  9. 老兵传奇:聊聊我在华为的20年
  10. 韩师师范学院计算机科学与技术在哪个学区,2017年韩山师范学院本科插班生考试《数据结构》A卷...
  11. centos6重启网络命令_Docker常用命令详解
  12. 洛谷——P1867 【Mc生存】经验值
  13. 技术改变世界以及减速慢行
  14. L1-003 个位数统计 (15 point(s))
  15. ProteusPro(EDA工具软件) V8.9 _下载及安装
  16. Windows下Postman安装以及汉化
  17. 珊瑚--shopee平台提现流程
  18. python NLP英式英语和美式英语的转换
  19. 什么是实体关系图(ERD)?
  20. linux内核 异常 log,Kernel Exception 问题分析

热门文章

  1. 【敬伟ps教程】蒙版和通道的基础知识
  2. erp系统的选择类型
  3. 打印机共享疑难杂症(不断更新...)
  4. react 同构 php,React状态管理与同构实战 PDF 影印版
  5. SCI期刊的检索及其影响因子
  6. 电脑某个磁盘空间不够,想进行磁盘扩展
  7. Eclipse快捷键大全(转载)
  8. E3游戏展十大观察:游戏行业缺乏惊喜1A1M
  9. windows10 缺失 msvcp140.dll 解决办法
  10. 【CSDN下载】第三期:多AGV调度系统软件、基于PCA的人脸识别系统