看门狗对于防止程序跑死是很关键的,很多时候我们的产品需要进入低功耗,而且唤醒间隔也比较长,此时如果看门狗启动了,那么就会导致处在低功耗的MCU发生复位。解决这个问题的方法有两种:
一种是增加看门狗的喂狗时间间隔,保证此间隔大于MCU唤醒间隔,这对于那种几个小时唤醒一次的应用,很多看门狗是不支持这么长的喂狗间隔的。第二种是MCU进入低功耗之后停止看门狗计时,唤醒之后重新开始。第二种方案就能很好的解决上述痛点,STM32系列很多单片机的看门狗就具备这个功能,拿STM32L476VET6这型号的单片机举例,此单片机的FLASH选项字节中的选项寄存器(FLASH_OPTR)里面有一位是用来设置在停止模式下是否冻结独立看门狗计数器的,还有一位是用来设置在待机模式下冻结独立看门狗计数器的(看下图红框内容)。因此只要将此位清零即可实现在停止模式或者待机模式下暂停看门狗的功能。
![](https://img-blog.csdnimg.cn/20201208135621128.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI3NzE4MjMx,size_16,color_FFFFFF,t_70)
下面是我的测试程序:
/*
* 函数名称: Watchdog_Init
* 函数说明: 软件看门狗初始化(喂狗间隔10s)
* 输入参数: 无
* 返回参数: 无
*/
void Watchdog_Init(void)
{
/* 配置用户选项字节:在停止模式下冻结独立看门狗计数器 */
FLASH_OBProgramInitTypeDef obprogram_init;
/* 读取用户选项字节 */
HAL_FLASHEx_OBGetConfig(&obprogram_init);
/* 判断FLASH_OPTR寄存器的IWDG_STOP位是否置位(不判断也行) */
if(obprogram_init.USERConfig & FLASH_OPTR_IWDG_STOP)
{
/* 置位则清零IWDG_STOP位 */
obprogram_init.OptionType=OPTIONBYTE_USER;
obprogram_init.USERType=OB_USER_IWDG_STOP;
obprogram_init.USERConfig=OB_IWDG_STOP_FREEZE;
/* 以下流程是根据手册上提供的 */
HAL_FLASH_Unlock();
HAL_FLASH_OB_Unlock();
HAL_FLASHEx_OBProgram(&obprogram_init);
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
/* OBL_LAUNCH:选项字节重载位,用来生效上述更改(如果OPTLOCK为0,将此位置1,则会导致复位,如果 OPTLOCK为1,则此位无法写入,MCU复位后此位默认置1) */
HAL_FLASH_OB_Launch();
}
/* 看门狗时钟源:LSI,大小为32KHz */
LL_IWDG_Enable(IWDG);
LL_IWDG_EnableWriteAccess(IWDG);
LL_IWDG_SetPrescaler(IWDG, LL_IWDG_PRESCALER_128); /* 分频后的计数频率为250HZ */
LL_IWDG_SetReloadCounter(IWDG, 2500); /* 10s */
while (LL_IWDG_IsReady(IWDG) != 1)
{
}
/* 设置窗口计数值,设为0xFFF关闭此功能 */
LL_IWDG_SetWindow(IWDG, 4095);
LL_IWDG_ReloadCounter(IWDG);
}
/*
* 函数名称: Enter_DeepSleep
* 函数说明: 进入停止模式
* 输入参数: 无
* 返回参数: 无
*/
void Enter_DeepSleep(void)
{
/* Clear all exti interrupt flag */
NVIC_DisableIRQ(SysTick_IRQn);
EXTI->PR1 = 0x007DFFFF;
EXTI->PR2 = 0x00000078;
LL_RTC_ClearFlag_WUT(RTC);
/* Set STOP2 mode when CPU enters deepsleep */
LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
/* Set SLEEPDEEP bit of Cortex System Control Register */
LL_LPM_EnableDeepSleep();
/* Request Wait For Interrupt */
__WFI();
/* Reset SLEEPDEEP bit of Cortex System Control Register */
SCB->SCR &= (uint32_t)~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
LL_RCC_HSE_Enable();
/* Wait till HSE is ready */
while(LL_RCC_HSE_IsReady() != 1)
{
}
LL_RCC_PLL_Enable();
/* Wait till PLL is ready */
while(LL_RCC_PLL_IsReady() != 1)
{
}
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
/* Wait till System clock is ready */
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
{
}
LL_Init1msTick(32000000);
LL_SetSystemCoreClock(32000000);
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), SYSTICK_PRIORITY, 0));
NVIC_EnableIRQ(SysTick_IRQn);
LL_SYSTICK_EnableIT();
}
经测试在低功耗下确实不会因为看门狗超时而出现不正常的复位。