STM32中待机模式与看门狗的冲突问题

2 篇文章 0 订阅
1 篇文章 0 订阅

最近在做刚刚入职的第一个STM32的项目,需要用到按键进入以及退出低功耗(待机模式),同时也要用到看门狗,排除进入低功耗待机模式的一些问题后加入看门狗,发现独立看门狗无法关闭,这样导致了待机模式无法进入。

首先介绍一下STM32低功耗,按功耗由高到低排列,STM32具有运行、睡眠、停止和待机四种工作模式。上电复位后 STM32 处于运行状态,当内核不需要继续运行,就可以选择进入后面的三种低功耗模式降低功耗,这三种模式中,电源消耗不同、唤醒时间不同、唤醒源不同,用户根据实际情况选用不同的低功耗模式,三种低功耗的模式说明如下图:

一、休眠模式

最开始做低功耗考虑的是休眠模式(SLEEP),在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,CM3核心的外设全都还照常运行,可以理解为代码停止运行了。如下代码可见,休眠模式需要关闭所有实时的内部中断,因为SLEEP模式会被所有的中断唤醒,因此在退出休眠模式之后需要重新使能被失能的中断。

void sleep(void)
{
    //printf("Start of sleep mode!\r\n");
    HAL_SuspendTick();
    HAL_TIM_Base_Stop_IT(&htim2);
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFE);
    //printf("End of sleep mode!\r\n");
    HAL_ResumeTick();
    HAL_TIM_Base_Start_IT(&htim2);
}

SLEEP模式相对来说比较简单但是局限性也比较大,如上代码中我关闭了TIM定时器中断才得以进入休眠模式,但是我的按键的长短按的判定是TIM定时器中断来判断的,因此我关闭了TIM中断我的开关就失效了,这时候只有一个办法,加入外部中断来唤醒SLEEP,也就是说我要再加一个按键中断来唤醒,但是我只有一个开关,或者说放弃定时器用延时来做开关的长度按,这样又会丧失一些精度。最后我还是决定换个方案试试看。

二、待机模式

因为看到STOP模式也会被中断唤醒(具体STOP是怎么样的我还没研究过),所有我先绕过它开始研究待机模式,正点原子上有个例程给我看傻了,他在中断里面加了三秒的延时,通常在中断子程序中是不调用延时子程序的,这样会增加中断处理时间,如果有其它低级中断了,就会延误响应中断了。这样是非常不规范的行为,不建议大家这样做。同时我也舍不得我的TIM定时器开关,所以就先自己琢磨了一下。

待机模式可实现系统的最低功耗。该模式是在Cortex-M3深睡眠模式时关闭电压调节器。整个1.8V供电区域被断电。PLL、HSI和HSE振荡器也被断电。SRAM和寄存器内容丢失。只有备份的寄存器和待机电路维持供电。

它除了关闭所有的时钟,还把 1.8V 区域的电源也完全关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测 boot 条件,相当于从头开始执行程序。

void Sys_Enter_Standby(void)
{
     __HAL_RCC_APB2_FORCE_RESET(); //复位所有IO
     __HAL_RCC_PWR_CLK_ENABLE(); //使能 PWR 时钟
     __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); //清除 Wake_UP 标志
     HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN3); //设置 WKUP 用于唤醒
     HAL_PWR_EnterSTANDBYMode(); //进入待机模式 
}

待机模式的唤醒条件:

当一个外部复位(NRST 引脚)、IWDG 复位、WKUP 引脚上的上升沿或 RTC 闹钟事件

发生时,微控制器从待机模式退出。

因为要配合按键所以当仁不让的的选择了WKUP引脚上升沿唤醒,这样做也遇到了个新的问题:第一次进入待机模式时瞬间被唤醒了,但是再次进入待机模式却不会出现这个问题,研究了一会发现是延时的问题,我做了一个长按3s以上进入待机模式,短按30ms退出休眠的设定,那么第一次进入休眠模式时除非使用者刚刚好长按3s,不然立即会被上升沿唤醒,多1ms都不行。这样是有问题的。后面也是找到了解决办法:

  if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == 0)
  {
    Sys_Enter_Standby();
  }

在main函数的While循环开始之前对WAKE_UP开关进行判定如果是低电平也就是未触发状态那么直接进入休眠模式,也就是代码烧写进去必须短按一下,退出休眠才能正常运行。让后长按进入休眠短按退出休眠在再无异常。那么休眠模式与按键的问题看起来就解决了。可以加入看门狗了,新的问题也随之而来。

最开始我加入了独立看门狗,但是大家可以看到,待机模式的唤醒条件中恰恰有IWDG复位,IWDG为什么复位?当然是因为没有喂狗,那么都待机了还拿什么喂狗。我最开始想到的是进入待机前关闭看门狗,但是通过不断百度得出结论,无法关闭,即使是待机,在感叹IWDG功能强大的同时我也开始头疼了。那么还有一个方案:RTC闹钟喂狗然后再次休眠,越想越扯了属于是,这样反复横跳对MCU长此以往必然是有损伤的,那还不如不做这个休眠。

最后发现貌似还有一个看门狗:WWDG,这个窗口看门狗看起来虽然复杂一些,但是貌似会被待机模式关闭,说干就干,这就用CUBE来一段:

#include "wwdg.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

WWDG_HandleTypeDef hwwdg;

/* WWDG init function */
void MX_WWDG_Init(void)
{

  hwwdg.Instance = WWDG;
  hwwdg.Init.Prescaler = WWDG_PRESCALER_8;
  hwwdg.Init.Window = 0x40;
  hwwdg.Init.Counter = 0x7f;
  hwwdg.Init.EWIMode = WWDG_EWI_ENABLE;
  if (HAL_WWDG_Init(&hwwdg) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_WWDG_MspInit(WWDG_HandleTypeDef* wwdgHandle)
{

  if(wwdgHandle->Instance==WWDG)
  {
  /* USER CODE BEGIN WWDG_MspInit 0 */

  /* USER CODE END WWDG_MspInit 0 */
    /* WWDG clock enable */
    __HAL_RCC_WWDG_CLK_ENABLE();

    /* WWDG interrupt Init */
    HAL_NVIC_SetPriority(WWDG_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(WWDG_IRQn);
  /* USER CODE BEGIN WWDG_MspInit 1 */

  /* USER CODE END WWDG_MspInit 1 */
  }
}

同时最后用中断喂狗,简单快捷:

void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg)
{
    //printf("feed dog!\r\n");
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
    HAL_WWDG_Refresh(hwwdg);//更新窗口看门狗
}

对了不要忘了启用提前喂狗中断以及使能看门狗中断:

最终效果(暂时用串口助手来展示一下):

代码烧录之后的状态(默认进入了待机模式):

按下WAKE-UP,代码正常打印:

长按WAKE_UP进入休眠:

如此也算是完美(避开问题罢了)解决了看门狗与待机模式的冲突。

(嵌入式新人,可能有一些地方说的不对,欢迎指正。)

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值