问题描述:在stm32L431cct6芯片上想启用看门狗,但配置完后发现一调用喂狗函数,就会导致单片机复位,调用一次复位一次。
问题背景:在原有HAL库工程上添加看门狗时,采用手动配置来初始化看门狗,并没有使用CubeMX生成的看门狗初始化函数,手动配置时缺少一个参数的配置,导致问题出现。
1. 手动配置代码如下
void MX_IWDG_Init(void)
{
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_64;
hiwdg.Init.Reload = 4095;
if (HAL_IWDG_Init(&hiwdg) != HAL_OK)
{
Error_Handler();
}
}
2. CubeMX生成的代码如下
static void MX_IWDG_Init(void)
{
/* USER CODE END IWDG_Init 1 */
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_64;
hiwdg.Init.Window = 4095; //CubeMX多配置的参数
hiwdg.Init.Reload = 4095;
if (HAL_IWDG_Init(&hiwdg) != HAL_OK)
{
Error_Handler();
}
}
可以看出区别就是CubeMX多配置了hiwdg.Init.Window,那么这是什么东西呢?
在回答这个问题之前,我们可以先看看数据手册(图1)如何描述L4系列的IWDG功能,主要注意红框内的内容,复位条件有两点:
1. 向下计数器的计数值到达0及0以下(看门狗计时溢出)
2. 向下计数器在窗口外被重置计数值(在窗口外喂狗)

根据第二点,我们就可以推断出`hiwdg.Init.Window`该参数是用来限制喂狗时机的,效果如下图:

数据手册里的说明更加详细:

在我手动初始化IWDG_WINR寄存器之后,喂狗便不再发生复位。
问题已解决,但仍有一点无法解释的地方,IWDG_WINR寄存器的复位值默认是4095(如上图3所示),理论上来讲即使我不手动配置它,喂狗也不会导致单片机复位,正如数据手册中所描述的,如果不更新该寄存器,那么window option是被禁用的。那么有没有可能是因为该值被更新了呢?
我们接着往下阅读参考手册,发现有禁用状态下配置IWDG的流程如下:
为了一探究竟,这里查看一下HAL库是如何初始化IWDG的,与上述描述有何异同
/**
* @brief Initialize the IWDG according to the specified parameters in the
* IWDG_InitTypeDef and start watchdog. Before exiting function,
* watchdog is refreshed in order to have correct time base.
* @param hiwdg pointer to a IWDG_HandleTypeDef structure that contains
* the configuration information for the specified IWDG module.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg)
{
uint32_t tickstart;
/* Check the IWDG handle allocation */
if (hiwdg == NULL)
{
return HAL_ERROR;
}
/* Check the parameters */
assert_param(IS_IWDG_ALL_INSTANCE(hiwdg->Instance));
assert_param(IS_IWDG_PRESCALER(hiwdg->Init.Prescaler));
assert_param(IS_IWDG_RELOAD(hiwdg->Init.Reload));
assert_param(IS_IWDG_WINDOW(hiwdg->Init.Window));
/* Enable IWDG. LSI is turned on automatically */
__HAL_IWDG_START(hiwdg);
/* Enable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers by writing
0x5555 in KR */
IWDG_ENABLE_WRITE_ACCESS(hiwdg);
/* Write to IWDG registers the Prescaler & Reload values to work with */
hiwdg->Instance->PR = hiwdg->Init.Prescaler;
hiwdg->Instance->RLR = hiwdg->Init.Reload;
/* Check pending flag, if previous update not done, return timeout */
tickstart = HAL_GetTick();
/* Wait for register to be updated */
while (hiwdg->Instance->SR != 0x00u)
{
if ((HAL_GetTick() - tickstart) > HAL_IWDG_DEFAULT_TIMEOUT)
{
return HAL_TIMEOUT;
}
}
/* If window parameter is different than current value, modify window
register */
if (hiwdg->Instance->WINR != hiwdg->Init.Window)
{
/* Write to IWDG WINR the IWDG_Window value to compare with. In any case,
even if window feature is disabled, Watchdog will be reloaded by writing
windows register */
hiwdg->Instance->WINR = hiwdg->Init.Window;
}
else
{
/* Reload IWDG counter with value defined in the reload register */
__HAL_IWDG_RELOAD_COUNTER(hiwdg);
}
/* Return function status */
return HAL_OK;
}
可以看出在初始化过程中,第6点有所不同(上述代码第50行)。HAL库在初始化时,先判断了下寄存器中的WINR值与句柄中Window的值,如果不同,那么会将句柄中的值赋值给寄存器。问题便出现在这里。
问题的根本原因:我们在定义iwdg句柄时一般为全局变量,导致hiwdg->Init.Window值为0,而寄存器中的值默认复位时为4065,由于两者不相等,所以寄存器中的值被赋值为0,再根据注释中所描述,写入window寄存器后会触发喂狗,而喂狗的时机一定是在窗口之外(窗口为0),进而导致单片机不断复位。
至此问题已彻底找到。
解决方案:在初始化有IWDG_WINR寄存器的单片机时应手动初始化hiwdg->Init.Window为4095