一、什么是窗口看门狗
窗口看门狗(Window watchdog,简称 WWDG)通常被用来监测由 外部干扰 或 不可预见的逻辑条件 造成的应用程序背离正常的运行序列而产生的软件故障。窗口看门狗它是一个能产生 系统复位信号 和 提前唤醒中断 的 递减计数器,不同的是它们的复位条件不一样。窗口看门狗产生复位信号有两个条件:
- 当递减计数器的数值从 0x40 减到 0x3F 时(T6位 跳变到 0)
- 喂狗的时候如果计数器的值大于 W[6:0] 时,此数值在 WWDG_CFR 寄存器定义
上述的两个条件详细解释是,当计数器的值减到 0x40 时还不喂狗的话,到下一个计数就会产生复位,这个值称为 窗口的下限值,是固定的值,不能改变。这个跟独立看门狗类似,不同的是窗口看门狗的计数器的值在减到某一个数之前喂狗的话也会产生复位,这个值叫 窗口的上限,上限值 W[6:0] 由用户设置。窗口看门狗计数器的上限值和下限值就是窗口的含义,喂狗也必须在窗口之内,否则就会复位。
窗口看门狗主要用于检测单片机程序运行时效是否精准,主要检测软件异常。它主要用于精准检测程序运行时间的场合。
二、窗口看门狗框图
WWDG 有一个来自 RCC 的 PCLK1 输入时钟,经过一个 4096 的分频器(4096 分频在设计时已经设定死了,图中并没有给出来,但我们可以通过查看寄存器 WWDG_CFR 的 WDGTB 位的描述知道),再经过一个分频系数可选(1、2、4、8)的可编程预分频器提供时钟给一个 7 位递减计数器,这里有两个输出信号。
结合寄存器分析窗口看门狗的上限值和下限值。W[6:0] 是 WWDG_CFR寄存器 的 低7位,用于与递减计数器 T[6:0] 比较的窗口值,也就是我们说的上限值,由用户设置。0x40 就是下限值,递减计数器达到这个值就会产生复位。T6位 就是 WWDG_CR 寄存器的位 6,即递减计数器 T[6:0] 的最高位。
三、窗口看门狗的工作原理
窗口看门狗就是一个递减计算器。在递减计数器的值递减过程中,当 T[6:0]>W[6:0] 是不允许刷新 T[6:0] 的值,即不允许喂狗,否则会产生复位。只有在 W[6:0]<T[6:0]<0x3F 这个时间可以喂狗,这就是喂狗的窗口时间。当 T[6:0]=0x3F,即 T6 位为 0 这一刻,也会产生复位。
上限值 W[6:0] 是由用户自己设置,但是一定要确保大于 0x40,否则就不存在上图的窗口了;
下限值 0x40 是固定的,不可修改。
四、窗口看门狗寄存器介绍
4.1、控制寄存器(WWDG_CR)
该寄存器只有 低八位 有效,其中 T[6:0] 用来存储看门狗的计数器的值,随时更新的,每隔(\(4096 * 2^{WDGTB[2:0]}\))PCLK 个周期减 1。当该计数器的值从 0x40 变为 0x3F 的时候,将产生看门狗复位。
WDGA 位则是看门狗的激活位,该位由软件置 1,启动看门狗,并且一定要注意的是该位一旦设置,就只能在硬件复位后才能清零了。
4.2、配置寄存器(WWDG_CFR)
该寄存器中的 EWI 位是提前唤醒中断,如果该位置 1,当递减计数器等于 0x40 时产生提前唤醒中断,我们就可以及时喂狗以避免 WWDG 复位。因此,我们一般都会用该位来设置中断。
注意这里在进入中断后,必须在不大于 1 个窗口看门狗计数周期的时间(在 pclk1 频率为 36M 且 WDGTB 为 0 的条件下,该时间为 113us)内重新写 WWDG_CR,否则,看门狗将产生复位!
用于使能窗口看门狗提前唤醒中断,设置预分频系数,设置窗口上限值;
4.3、状态寄存器(WWDG_SR)
该寄存器用来记录当前是否有提前唤醒的标志。该寄存器仅有位 0 有效,其他都是保留位。当计数器值达到 0x40 时,此位由硬件 置 1。它必须通过软件写 0 来清除。对此位写 1 无效。即使中断未被使能,在计数器的值达到 0x40 的时候,此位也会被置 1。
五、窗口看门狗超时时间计算
窗口看门狗的超时时间计算公式:\(T_{out} = \frac{4096 * 2^{WDGTB} * (T[5:0] +1)}{F_{WWDG}}\)
其中,\(T_{out}\) 是 WWDG 的超时时间(没喂狗);\(F_{WWDG}\) 是 WWDG 的是时钟源频率,它来自于 PCLK1 总线时钟(36MHz);4096 是 WWDG 固定的预分频系数;\(2^{WDGTB}\) 是 WWDG_CRF 寄存器设置的预分频系数;\(T[5:0]\) 是 WWDG 计数器的低 6 位;
根据以上公式,可以得到最小-最大超时时间表如下表所示:
WDGTB | 最小超时值 | 最大超时值 |
---|---|---|
0 | 113μs | 7.28ms |
1 | 227μs | 14.56ms |
2 | 455μs | 29.12ms |
3 | 910μs | 58.25ms |
六、窗口看门狗配置步骤
WWDG 在 HAL 库中的驱动代码在 stm32f1xx_hal_wwdg.c 文件及其头文件 stm32f1xx_hal_wwdg.h 中。
6.1、使能WWDG的时钟
/** @defgroup RCC_APB1_Clock_Enable_Disable APB1 Clock Enable Disable
* @brief Enable or disable the Low Speed APB (APB1) peripheral clock.
* @note After reset, the peripheral clock (used for registers read/write access)
* is disabled and the application software has to enable this clock before
* using it.
* @{
*/
#define __HAL_RCC_WWDG_CLK_ENABLE() do { \
__IO uint32_t tmpreg; \
SET_BIT(RCC->APB1ENR, RCC_APB1ENR_WWDGEN);\
/* Delay after an RCC peripheral clock enabling */\
tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_WWDGEN);\
UNUSED(tmpreg); \
} while(0U)
6.2、WWDG工作参数初始化
【1】、HAL_WWDG_Init() 函数
IWDG 的初始化函数,它主要用于 设置 窗口值,分频数 和 计数器初始值、开启 WWDG,其声明如下:
/**
* @brief Initialize the WWDG according to the specified.
* parameters in the WWDG_InitTypeDef of associated handle.
* @param hwwdg pointer to a WWDG_HandleTypeDef structure that contains
* the configuration information for the specified WWDG module.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_WWDG_Init(WWDG_HandleTypeDef *hwwdg)
- 函数形参:
hwwdg 是 WWDG 句柄,WWDG_HandleTypeDef 结构体类型,其定义如下:
/**
* @brief WWDG handle Structure definition
*/
#if (USE_HAL_WWDG_REGISTER_CALLBACKS == 1)
typedef struct __WWDG_HandleTypeDef
#else
typedef struct
#endif /* USE_HAL_WWDG_REGISTER_CALLBACKS */
{
WWDG_TypeDef *Instance; /*!< Register base address */
WWDG_InitTypeDef Init; /*!< WWDG required parameters */
#if (USE_HAL_WWDG_REGISTER_CALLBACKS == 1)
void (* EwiCallback)(struct __WWDG_HandleTypeDef *hwwdg); /*!< WWDG Early WakeUp Interrupt callback */
void (* MspInitCallback)(struct __WWDG_HandleTypeDef *hwwdg); /*!< WWDG Msp Init callback */
#endif /* USE_HAL_WWDG_REGISTER_CALLBACKS */
} WWDG_HandleTypeDef;
Instance:指向 WWDG 寄存器基地址。
#define WWDG ((WWDG_TypeDef *)WWDG_BASE)
Init:WWDG初始化结构体,用于配置计数器的相关参数。WWDG_InitTypeDef 这个结构体类型定义如下:
/**
* @brief WWDG Init structure definition
*/
typedef struct
{
uint32_t Prescaler; /*!< Specifies the prescaler value of the WWDG.
This parameter can be a value of @ref WWDG_Prescaler */
uint32_t Window; /*!< Specifies the WWDG window value to be compared to the downcounter.
This parameter must be a number Min_Data = 0x40 and Max_Data = 0x7F */
uint32_t Counter; /*!< Specifies the WWDG free-running downcounter value.
This parameter must be a number between Min_Data = 0x40 and Max_Data = 0x7F */
uint32_t EWIMode ; /*!< Specifies if WWDG Early Wakeup Interrupt is enable or not.
This parameter can be a value of @ref WWDG_EWI_Mode */
} WWDG_InitTypeDef;
Prescaler:预分频系数,WWDG_PRESCALER_1、WWDG_PRESCALER_2、WWDG_PRESCALER_4、WWDG_PRESCALER_8 四个值,分别表示 1、2、4、8分频。
/** @defgroup WWDG_Prescaler WWDG Prescaler
* @{
*/
#define WWDG_PRESCALER_1 0x00000000u /*!< WWDG counter clock = (PCLK1/4096)/1 */
#define WWDG_PRESCALER_2 WWDG_CFR_WDGTB_0 /*!< WWDG counter clock = (PCLK1/4096)/2 */
#define WWDG_PRESCALER_4 WWDG_CFR_WDGTB_1 /*!< WWDG counter clock = (PCLK1/4096)/4 */
#define WWDG_PRESCALER_8 (WWDG_CFR_WDGTB_1 | WWDG_CFR_WDGTB_0) /*!< WWDG counter clock = (PCLK1/4096)/8 */
Window:窗口值,即上限值。
Counter:计数器值,用于保存要设置计数器的值。
EWIMode:提前唤醒中断使能。
/** @defgroup WWDG_EWI_Mode WWDG Early Wakeup Interrupt Mode
* @{
*/
#define WWDG_EWI_DISABLE 0x00000000u /*!< EWI Disable */
#define WWDG_EWI_ENABLE WWDG_CFR_EWI /*!< EWI Enable */
- 函数返回值:
HAL_StatusTypeDef 枚举类型的值。
/**
* @brief HAL Status structures definition
*/
typedef enum
{
HAL_OK = 0x00U,
HAL_ERROR = 0x01U,
HAL_BUSY = 0x02U,
HAL_TIMEOUT = 0x03U
} HAL_StatusTypeDef;
6.3、设置优先级,使能中断
设置 NVIC 分为三步,即:设置优先级分组、设置优先级、使能中断。
中断分组 使用 HAL_NVIC_SetPriorityGrouping() 函数设置,中断优先级 使用 HAL_NVIC_SetPriority() 函数设置,中断使能 使用 HAL_NVIC_EnableIRQ() 函数设置。
【1】、HAL_NVIC_SetPriorityGrouping() 函数
/**
* @brief Sets the priority grouping field (preemption priority and subpriority)
* using the required unlock sequence.
* @param PriorityGroup: The priority grouping bits length.
* This parameter can be one of the following values:
* @arg NVIC_PRIORITYGROUP_0: 0 bits for preemption priority
* 4 bits for subpriority
* @arg NVIC_PRIORITYGROUP_1: 1 bits for preemption priority
* 3 bits for subpriority
* @arg NVIC_PRIORITYGROUP_2: 2 bits for preemption priority
* 2 bits for subpriority
* @arg NVIC_PRIORITYGROUP_3: 3 bits for preemption priority
* 1 bits for subpriority
* @arg NVIC_PRIORITYGROUP_4: 4 bits for preemption priority
* 0 bits for subpriority
* @note When the NVIC_PriorityGroup_0 is selected, IRQ preemption is no more possible.
* The pending IRQ priority will be managed only by the subpriority.
* @retval None
*/
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);
- 函数参数:
PriorityGroup 是中断优先级分组号,可以选择范围:NVIC_PRIORITYGROUP_0 到 NVIC_PRIORITYGROUP_4(共5组);
/** @defgroup CORTEX_Preemption_Priority_Group CORTEX Preemption Priority Group
* @{
*/
#define NVIC_PRIORITYGROUP_0 0x00000007U /*!< 0 bits for pre-emption priority
4 bits for subpriority */
#define NVIC_PRIORITYGROUP_1 0x00000006U /*!< 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PRIORITYGROUP_2 0x00000005U /*!< 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PRIORITYGROUP_3 0x00000004U /*!< 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PRIORITYGROUP_4 0x00000003U /*!< 4 bits for pre-emption priority
0 bits for subpriority */
- 函数返回值:
无;
【2】、HAL_NVIC_SetPriority() 函数
/**
* @brief Sets the priority of an interrupt.
* @param IRQn: External interrupt number.
* This parameter can be an enumerator of IRQn_Type enumeration
* (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f10xx.h))
* @param PreemptPriority: The preemption priority for the IRQn channel.
* This parameter can be a value between 0 and 15
* A lower priority value indicates a higher priority
* @param SubPriority: the subpriority level for the IRQ channel.
* This parameter can be a value between 0 and 15
* A lower priority value indicates a higher priority.
* @retval None
*/
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
- 函数参数:
IRQn 是中断号,可以选择范围:IRQn_Type 定义的枚举类型,定义在 stm32f103xe.h;
/**
* @brief STM32F10x Interrupt Number Definition, according to the selected device
* in @ref Library_configuration_section
*/
/*!< Interrupt Number Definition */
typedef enum
{
/****** STM32 specific Interrupt Numbers *********************************************************/
WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */
} IRQn_Type;
PreemptPriority 是 抢占优先级,可以选择范围:0 到 15;
SubPriority 是 响应优先级,可以选择范围:0 到 15;
- 函数返回值:
无;
【3】、HAL_NVIC_EnableIRQ() 函数
/**
* @brief Enables a device specific interrupt in the NVIC interrupt controller.
* @note To configure interrupts priority correctly, the NVIC_PriorityGroupConfig()
* function should be called before.
* @param IRQn External interrupt number.
* This parameter can be an enumerator of IRQn_Type enumeration
* (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f10xxx.h))
* @retval None
*/
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
- 函数参数:
IRQn 是中断号,可以选择范围:IRQn_Type 定义的枚举类型,定义在 stm32f103xe.h;
/**
* @brief STM32F10x Interrupt Number Definition, according to the selected device
* in @ref Library_configuration_section
*/
/*!< Interrupt Number Definition */
typedef enum
{
/****** STM32 specific Interrupt Numbers *********************************************************/
WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */
} IRQn_Type;
- 函数返回值:
无;
6.4、编写中断服务函数
【1】、WWDG_IRQHandler() 函数
每开启一个中断,就必须编写其对应的中断服务函数,否则将导致死机(CPU 将找不到中断服务函数)。中断服务函数接口厂家已经在startup_stm32f103xe.s 中做好了,STM32F1 的 窗口看门狗的中断函数定义如下:
void WWDG_IRQHandler();
HAL库 定义了一个 WWDG 中断处理共用函数 HAL_WWDG_IRQHandler(),我们在 WWDG 中断服务函数中会调用该函数。
/**
* @brief Handle WWDG interrupt request.
* @note The Early Wakeup Interrupt (EWI) can be used if specific safety operations
* or data logging must be performed before the actual reset is generated.
* The EWI interrupt is enabled by calling HAL_WWDG_Init function with
* EWIMode set to WWDG_EWI_ENABLE.
* When the downcounter reaches the value 0x40, and EWI interrupt is
* generated and the corresponding Interrupt Service Routine (ISR) can
* be used to trigger specific actions (such as communications or data
* logging), before resetting the device.
* @param hwwdg pointer to a WWDG_HandleTypeDef structure that contains
* the configuration information for the specified WWDG module.
* @retval None
*/
void HAL_WWDG_IRQHandler(WWDG_HandleTypeDef *hwwdg)
{
/* Check if Early Wakeup Interrupt is enable */
if (__HAL_WWDG_GET_IT_SOURCE(hwwdg, WWDG_IT_EWI) != RESET)
{
/* Check if WWDG Early Wakeup Interrupt occurred */
if (__HAL_WWDG_GET_FLAG(hwwdg, WWDG_FLAG_EWIF) != RESET)
{
/* Clear the WWDG Early Wakeup flag */
__HAL_WWDG_CLEAR_FLAG(hwwdg, WWDG_FLAG_EWIF);
#if (USE_HAL_WWDG_REGISTER_CALLBACKS == 1)
/* Early Wakeup registered callback */
hwwdg->EwiCallback(hwwdg);
#else
/* Early Wakeup callback */
HAL_WWDG_EarlyWakeupCallback(hwwdg);
#endif /* USE_HAL_WWDG_REGISTER_CALLBACKS */
}
}
}
其中,hwwdg 是 WWDG 句柄,WWDG_HandleTypeDef 结构体类型。这里,我们使用它的 Instance 成员,Instance 成员的取值为:
#define WWDG ((WWDG_TypeDef *)WWDG_BASE)
6.5、重定义提前唤醒回调函数
【1】、HAL_WWDG_EarlyWakeupCallback() 函数
WWDG 中断处理共用函数 HAL_WWDG_IRQHandler() 会调用回调函数 HAL_WWDG_EarlyWakeupCallback(),提前唤醒中断逻辑我们写在回调函数HAL_WWDG_EarlyWakeupCallback() 中。
/**
* @brief WWDG Early Wakeup callback.
* @param hwwdg pointer to a WWDG_HandleTypeDef structure that contains
* the configuration information for the specified WWDG module.
* @retval None
*/
__weak void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hwwdg);
/* NOTE: This function should not be modified, when the callback is needed,
the HAL_WWDG_EarlyWakeupCallback could be implemented in the user file
*/
}
- 函数参数:
hwwdg 是 WWDG 句柄,WWDG_HandleTypeDef 结构体类型。这里,我们使用它的 Instance 成员,Instance 成员的取值为:
#define WWDG ((WWDG_TypeDef *)WWDG_BASE)
- 函数返回值:
无;
6.6、在窗口期内喂狗
【1】、HAL_WWDG_Refresh() 函数
/**
* @brief Refresh the WWDG.
* @param hwwdg pointer to a WWDG_HandleTypeDef structure that contains
* the configuration information for the specified WWDG module.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_WWDG_Refresh(WWDG_HandleTypeDef *hwwdg);
- 函数参数:
hwwdg 是 WWDG 句柄,WWDG_HandleTypeDef 结构体类型。这里,我们使用它的 Instance 成员,Instance 成员的取值为:
#define WWDG ((WWDG_TypeDef *)WWDG_BASE)
- 函数返回值:
HAL_StatusTypeDef 枚举类型的值。
/**
* @brief HAL Status structures definition
*/
typedef enum
{
HAL_OK = 0x00U,
HAL_ERROR = 0x01U,
HAL_BUSY = 0x02U,
HAL_TIMEOUT = 0x03U
} HAL_StatusTypeDef;
七、程序设计
本程序利用窗口看门狗的特性,配置一个合适的窗口时间,并开启了提前唤醒中断,如果程序未在合适的时间喂狗,则会触发窗口看门狗中断。
WWDG 初始化函数:
WWDG_HandleTypeDef hwwdg;
void WWDG_Init(void)
{
hwwdg.Instance = WWDG; // 窗口看门狗
hwwdg.Init.Prescaler = WWDG_PRESCALER_8; // 设置分频系数
hwwdg.Init.Window = 0x5f; // 设置窗口值
hwwdg.Init.Counter = 0x7f; // 设置计数器值
hwwdg.Init.EWIMode = WWDG_EWI_ENABLE; // 使能窗口看门狗提前唤醒中断
HAL_WWDG_Init(&hwwdg); // 初始化WWDG
}
窗口看门狗的预分频值设置为 8,时钟来源于 PCLK1(36MHz),窗口期下限值为 ,假设窗口看门狗定时初始值为 0x7F,窗口上限值为 0x5F,根据公式:\(T_{out} = \frac{4096 * 2^{WDGTB} * (T[5:0] + 1)}{f_{WWDG}}\) 可得:窗口上限时间为:\(T_{out} = \frac{4096 * 8 * (0x7F - 0x5F)}{36000} ≈ 29.12ms\);窗口下限时间:\(T_{out} = \frac{4096 *8 * (0x7F - 0x3F)}{36000} ≈ 58.25ms\),即 喂狗的窗口区间为 29.12~58.25ms。
WWDG MSP 回调函数:
void HAL_WWDG_MspInit(WWDG_HandleTypeDef* wwdgHandle)
{
if(wwdgHandle->Instance==WWDG) // 判断是否是窗口看门狗
{
/* WWDG clock enable */
__HAL_RCC_WWDG_CLK_ENABLE(); // 使能窗口看门狗时钟
/* WWDG interrupt Init */
HAL_NVIC_SetPriority(WWDG_IRQn, 2, 2); // 设置抢占优先级和响应优先级
HAL_NVIC_EnableIRQ(WWDG_IRQn); // 使能窗口看门狗中断
}
}
WWDG 中断服务函数:
void WWDG_IRQHandler(void)
{
HAL_WWDG_IRQHandler(&hwwdg);
}
WWDG 提前唤醒回调函数:
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
HAL_WWDG_Refresh(hwwdg); // 喂狗
printf("你已经进行了喂狗!\r\n");
}
配置 串口工作参数函数 和 串口底层初始化函数 请在 串口通信 篇章查看;
main() 函数:
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
USART1_Init();
if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST) != RESET) // 窗口看门狗复位
{
printf("窗口看门狗复位!\r\n");
__HAL_RCC_CLEAR_RESET_FLAGS(); // 清除标志位
}
else
{
printf("外部复位!\r\n");
}
HAL_Delay(500);
printf("请在窗口期内喂狗!\r\n");
WWDG_Init();
while (1)
{
HAL_Delay(60);
}
}
标签:NVIC,HAL,看门狗,15,WWDG,窗口,hwwdg
From: https://www.cnblogs.com/kurome/p/17625378.html