文章目录
前言
使用STM32CubeIDE实现窗口看门狗实验。
硬件:STM32F103C8T6最小系统板 + USB转TTL模块
软件:STM32CubeIDE
一、实验目的
实现窗口看门狗实验,验证窗口期喂狗,可不执行复位
若不及时喂狗,串口输出 “ 独立看门狗复位!”。
预设:计数器初始值为0x7F,窗口上限值为0x5F,预分频系数为8。
二、学习内容
- 使用窗口看门狗( WWDG )
三、实践操作
1.窗口看门狗(WWDG)
WWDG简介
WWDG( Windows Watchdog ),窗口看门狗。
本质为能产生能产生系统复位信号和提前唤醒中断的计数器。
WWDG特性:
- 递减的计数器;
- 当递减计数器值从 0x40 ( 64 ) 减到 0x3F ( 63 ) 时复位( 即T6位跳变到0 );
- 计数器的值大于 W[6:0] 值时喂狗会复位;
- 提前唤醒中断( EWI ):当递减计数器等于 0x40 时可产生
喂狗操作:在窗口期内重装载计数器的值,防止复位
WWDG作用:用于监测单片机程序运行时效是否精准,主要检测软件异常;用于需要精准检测程序运行时间的场合。
WWDG工作原理
计数器的初始值是用户自己设定的,T[6:0]共7位,最大为0x7F(1111111);
窗口上限值也是用户自己设定的,W[6:0]共7位,最大为0x7F(1111111);
当计数值从初始值递减到0x40( 无法修改 )时,可产生提前唤醒中断( EWI );
窗口下限值为0x3F( 无法修改 ),当计数值从初始值递减到窗口下限值时,产生复位;
可以考虑在提前唤醒中断中进行喂狗操作
在窗口期内可以喂狗( 窗口上限值 ~ 窗口下限值 );在非窗口期不可以进行喂狗,否则会产生复位
因此窗口期一定要满足W[6:0] ≥ 窗口期 > 0x3F
。
所以窗口看门狗有两种情况会产生复位:1.到窗口下限值了还没喂狗;2.在非窗口期强行喂狗
强调的是程序运行时间的精准,多一点或者少一点都有可能导致复位!
WWDG超时时间计算
其中:
Tout 是WWDG超时时间;
fWWDG 是WWDG的时钟频率,STM32F103系列的fWWDG为36MHz( PCLK1 );
2^WDFGTB 是预分频系数数值( 1,2,4,8 )
T[5:0] 是WWDG_CR寄存器后6位的值( 0~63 )
STM32F103的窗口看门狗超时时间表
预分频系数数值 | WDGTB | 最小超时值(us) | 最大超时值(ms) |
---|---|---|---|
1 | 0 | 113 | 7.28 |
2 | 1 | 227 | 14.56 |
4 | 2 | 455 | 29.12 |
8 | 3 | 910 | 58.25 |
IWDG与WWDG的主要区别(STM32F103系列)
对比点 | IWDG | WWDG |
---|---|---|
时钟源 | LSI(40KHz),独立RC振荡器提供 | PCLK1(36MHz),外部高速晶振(8MHz)提供 |
复位条件 | 递减计数到0 | 计数值大于W[6:0]值喂狗/递减计数到0x3F |
中断 | 无 | 递减计数到0x40可产生提前唤醒中断( EWI ) |
递减计数器位数 | 12位(max:4096~0) | 7位(max:127~63) |
应用场景 | 防止程序跑飞,死循环,死机 | 检测程序时效,防止软件异常 |
具体了解IWDG可移步至上一篇文章:7.STM32CubeIDE独立看门狗实验
3.硬件介绍
硬件:STM32F103C8T6最小系统板 + USB转TTL模块
利用USART2进行串口输出( PA2->USART_TX / PA3->USART_RX )
实物图如下图所示:
注意:两个设备之间RX->TX / TX->RX
4.软件介绍
首先新建工程并进行初始化配置
在此不进行赘述,详细步骤可移步至之前文章。
与之前文章不同的是,根据预设条件:计数器初始值为0x7F、窗口上限值为0x5F、预分频系数为8,需要配置WWDG,如下图所示:
根据预设条件可以算出,超时时间Tout = 58.25ms,窗口时间为29.12ms,因此喂狗时间为:58.25 > 喂狗时间 ≥ 29.13。
为了防止复位,需要在该时间段进行喂狗或用提前换醒中断进行喂狗。
接下来进行窗口看门狗实验的代码编程
代码逻辑与独立看门狗类似,其中判断是否为WWDG复位可以通过__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST) != RESET
判断,因为当RCC_FLAG_WWDGRST
这个位为1时,证明是通过WWDG复位的,其他系统复位也有相应的标志位:
#define RCC_FLAG_LSIRDY ((uint8_t)((CSR_REG_INDEX << 5U) | RCC_CSR_LSIRDY_Pos)) /*!< Internal Low Speed oscillator Ready */
#define RCC_FLAG_PINRST ((uint8_t)((CSR_REG_INDEX << 5U) | RCC_CSR_PINRSTF_Pos)) /*!< PIN reset flag */
#define RCC_FLAG_PORRST ((uint8_t)((CSR_REG_INDEX << 5U) | RCC_CSR_PORRSTF_Pos)) /*!< POR/PDR reset flag */
#define RCC_FLAG_SFTRST ((uint8_t)((CSR_REG_INDEX << 5U) | RCC_CSR_SFTRSTF_Pos)) /*!< Software Reset flag */
#define RCC_FLAG_IWDGRST ((uint8_t)((CSR_REG_INDEX << 5U) | RCC_CSR_IWDGRSTF_Pos)) /*!< Independent Watchdog reset flag */
#define RCC_FLAG_WWDGRST ((uint8_t)((CSR_REG_INDEX << 5U) | RCC_CSR_WWDGRSTF_Pos)) /*!< Window watchdog reset flag */
#define RCC_FLAG_LPWRRST ((uint8_t)((CSR_REG_INDEX << 5U) | RCC_CSR_LPWRRSTF_Pos)) /*!< Low-Power reset flag */
其中__HAL_RCC_GET_FLAG(__FLAG__)
是HAL库自带的检测RCC寄存器中某一个标志位状态的函数,具体如下:
/** @brief Check RCC flag is set or not.
* @param __FLAG__ specifies the flag to check.
* This parameter can be one of the following values:
* @arg @ref RCC_FLAG_HSIRDY HSI oscillator clock ready.
* @arg @ref RCC_FLAG_HSERDY HSE oscillator clock ready.
* @arg @ref RCC_FLAG_PLLRDY Main PLL clock ready.
@if STM32F105xx
* @arg @ref RCC_FLAG_PLL2RDY Main PLL2 clock ready.
* @arg @ref RCC_FLAG_PLLI2SRDY Main PLLI2S clock ready.
@elsif STM32F107xx
* @arg @ref RCC_FLAG_PLL2RDY Main PLL2 clock ready.
* @arg @ref RCC_FLAG_PLLI2SRDY Main PLLI2S clock ready.
@endif
* @arg @ref RCC_FLAG_LSERDY LSE oscillator clock ready.
* @arg @ref RCC_FLAG_LSIRDY LSI oscillator clock ready.
* @arg @ref RCC_FLAG_PINRST Pin reset.
* @arg @ref RCC_FLAG_PORRST POR/PDR reset.
* @arg @ref RCC_FLAG_SFTRST Software reset.
* @arg @ref RCC_FLAG_IWDGRST Independent Watchdog reset.
* @arg @ref RCC_FLAG_WWDGRST Window Watchdog reset.
* @arg @ref RCC_FLAG_LPWRRST Low Power reset.
* @retval The new state of __FLAG__ (TRUE or FALSE).
*/
#define __HAL_RCC_GET_FLAG(__FLAG__) (((((__FLAG__) >> 5U) == CR_REG_INDEX)? RCC->CR : \
((((__FLAG__) >> 5U) == BDCR_REG_INDEX)? RCC->BDCR : \
RCC->CSR)) & (1U << ((__FLAG__) & RCC_FLAG_MASK)))
因此可以通过以下代码判断是否为WWDG复位:
if( __HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST) != RESET ) /* 判断是否为窗口看门狗复位 */
{
printf("窗口看门狗复位!\r\n");
__HAL_RCC_CLEAR_RESET_FLAGS(); /* 清除复位标志位 */
}
else
{
printf("其他复位!\r\n");
}
不希望这段代码一直循环,只希望提醒一次,因此需要将其放在主循环while(1)
之前。
并且每次复位之后,一定要将WWDG复位标志位置0,否则后面每次复位都会显示是WWDG复位;可通过__HAL_RCC_CLEAR_RESET_FLAGS()
函数对复位标志位进行置0。
接下来在主循环while(1)
中进行验证。
不及时喂狗:
while (1)
{
/* 在主程序中喂狗 */
HAL_Delay(60); /* 58.25 > 喂狗时间 ≥ 29.13 */
HAL_WWDG_Refresh(&hwwdg); /* 喂狗 */
printf("已经喂狗!\r\n");
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
通过串口调试助手可以看到:
由于不及时喂狗,程序不断地进行WWDG复位,其中打印的其他复位
是按下复位键进行区别。
过早喂狗:
while (1)
{
/* 在主程序中喂狗 */
HAL_Delay(20); /* 58.25 > 喂狗时间 ≥ 29.13 */
HAL_WWDG_Refresh(&hwwdg); /* 喂狗 */
printf("已经喂狗!\r\n");
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
通过串口调试助手可以看到:由于过早喂狗,程序不断地进行WWDG复位,其中打印的其他复位
是按下复位键进行区别。
窗口期喂狗:
while (1)
{
/* 在主程序中喂狗 */
HAL_Delay(40); /* 58.25 > 喂狗时间 ≥ 29.13 */
HAL_WWDG_Refresh(&hwwdg); /* 喂狗 */
printf("已经喂狗!\r\n");
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
通过串口调试助手可以看到:在窗口期内喂狗,程序不会进行WWDG复位,程序正常运行,其中打印的其他复位
是按下复位键进行区别。
提前唤醒中断( EWI )喂狗:
首先要使能中断,并配置优先级,如下图所示:
其次需要编写提前唤醒中断回调函数void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
:
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
HAL_WWDG_Refresh(hwwdg); /* 喂狗 */
printf("中断喂狗!\r\n");
}
在中断喂狗之后,就不需要在主函数中进行喂狗了!
通过串口调试助手可以看到:
总结
本篇介绍了如何实现一个窗口看门狗实验,分别从硬件方面与软件方面阐述,在硬件方面主要介绍了硬件组成,在软件方面主要介绍了如何验证窗口看门狗是否喂狗成功,并且分为在主函数内喂狗和提前唤醒中断喂狗。完美完成本文开篇提到的学习任务与功能!
在实际应用中,窗口看门狗往往是检测软件是否发生异常!