【STM32笔记】HAL库低功耗STOP停止模式的串口唤醒(解决进入以后立马唤醒、串口唤醒和回调无法一起使用的问题)
前文:
blog.csdn.net/weixin_53403301/article/details/128216064
【STM32笔记】HAL库低功耗模式配置(ADC唤醒无法使用、低功耗模式无法烧录解决方案)
低功耗模式如图所示
停止模式有三种 分别是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线 串口初始化时钟也必须要配置为HSI
UART_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);
}
}
进入以后立马唤醒
若是串口悬空 或硬件设计问题 串口数据不定 则可能进入以后立马被唤醒
在外部硬件上加下拉 或者软件配置下拉(或上拉)即可 不过下拉更省电
串口唤醒和回调无法一起使用的问题
在调试时 发现串口唤醒和回调无法一起使用 进入了回调以后就退出了 不会进入串口唤醒
其实就是
的问题
若不使用这个语句 虽然串口可以用 也能接收数据并返回 但是进不了唤醒回调
其实就是因为时序被改变了
进入低功耗以后 接收数据唤醒 则先进入接收回调 然后发一次数据 但不会进行唤醒 因为时序有问题 mcu认为接收的数据不正常
保留这个语句以后就好了
另外 每次进入低功耗前 都要先调用
语句 否则无法正常唤醒 也不能再次进入低功耗模式
为了避免出错 每次唤醒以后都应该清空调唤醒中断
STOP模式会关闭时钟 所以建议是回调以后就初始化时钟一次
Ctrl_UART_StopMode_WakeUp(&huart2,true);
Enter_Low_PWR(2,0);
LPUART的配置同理 完全一模一样的语句