介绍
STM32的"看门狗"(Watchdog)是一种硬件安全特性,用于监控STM32微控制器的正常操作。当系统出现故障或异常时,看门狗能够检测到这些情况,并采取相应的措施,通常是重置微控制器,以防止系统陷入无效状态。
看门狗的基本工作原理是这样的:
-
定时器:看门狗内置了一个定时器,当看门狗被启用时,这个定时器开始计数。
-
喂狗:在程序运行过程中,必须定期地“喂狗”,即重置看门狗的定时器。这通常通过写入特定的寄存器来完成。
-
超时和复位:如果在设定的时间间隔内没有喂狗,看门狗会认为程序可能出现了异常,无法正常执行。这时,看门狗将引发一个超时事件,默认情况下,这个事件会导致微控制器复位,从而让系统有机会重新启动并恢复正常操作。
STM32微控制器通常包含两个看门狗:
-
独立看门狗(IWDG)
独立看门狗(Independent Watchdog)是一个由独立时钟源供电的看门狗,它的时钟源通常是内置的低速时钟(LSI)振荡器。由于它不依赖于主时钟系统,即使在主时钟发生故障的情况下,独立看门狗也能正常工作。这使得它在需要极高可靠性的应用中非常有用,例如那些对系统故障容忍度很低的环境。
独立看门狗的操作相对简单,它通常有一个可编程的计数器,当计数器减到0时,如果没有重新加载计数器(即“喂狗”),看门狗就会触发系统复位。由于它的独立性,独立看门狗通常用于监控那些对系统稳定性至关重要的任务。
-
窗口看门狗(WWDG)
窗口看门狗(Window Watchdog)提供了一个时间窗口,喂狗操作必须在这个窗口内进行。这种设计可以防止由于喂狗操作过早或过晚而导致的系统问题。窗口看门狗通常用于需要精确监控的应用,它可以在看门狗超时之前提供一个预警窗口,允许系统有机会采取一些预防措施。
窗口看门狗的时钟来源通常是主时钟,这使得它在主时钟正常工作时非常有效。窗口看门狗的超时时间较短,这意味着它可以在系统出现问题时快速响应。
区别
-
时钟源:
- IWDG:使用独立的时钟源,通常是内置的低速内部时钟(LSI)振荡器。这意味着即使主时钟发生故障,IWDG仍然可以独立运行,确保系统的可靠性。
- WWDG:通常使用主时钟(如HCLK)的分频值作为时钟源。因此,WWDG的精度和稳定性与主时钟系统相关联。
-
灵活性:
- IWDG:提供固定的超时时间,用户可以通过编程设置不同的超时周期,但相对于WWDG来说,灵活性较低。
- WWDG:提供一个可编程的时间窗口,允许在一定的范围内调整超时时间。这可以在看门狗超时之前提供一个预警窗口,让系统有机会采取预防措施。
-
用途:
- IWDG:由于其独立性和简单的操作,通常用于监控那些对系统稳定性至关重要的任务,特别是在那些对系统故障容忍度很低的环境。
- WWDG:由于其窗口特性,可以用于需要精确监控的应用,确保系统在规定的时间内正常运行,防止由于喂狗操作过早或过晚而导致的系统问题。
-
喂狗操作:
- IWDG:当计数器减到0时,如果没有重新加载计数器(即“喂狗”),看门狗就会触发系统复位。
- WWDG:喂狗操作必须在窗口期内进行,如果过早或过晚,看门狗会认为系统没有正确运行,并触发系统复位。
-
响应时间:
- IWDG:由于使用的是低速时钟,其响应时间相对较长。
- WWDG:使用主时钟的分频值,响应时间相对较短,适合快速检测和响应系统异常。
应用
独立看门狗(IWDG)的应用举例:
- 远程监控系统:在远程监控系统中,特别是在那些维护困难或成本高昂的环境中,如海底监控、远程气象站等,IWDG可以确保系统在极端条件下也能稳定运行。
- 医疗设备:在生命支持系统中,如心脏起搏器或呼吸机,系统的可靠性至关重要。IWDG可以保证即使在主时钟故障的情况下,设备也能安全地重置并继续工作。
- 无人驾驶车辆:在自动驾驶汽车或无人机中,IWDG可以作为一个最后的安全防线,确保控制系统在出现任何问题时都能恢复到已知的安全状态。
窗口看门狗(WWDG)的应用举例:
- 实时操作系统(RTOS):在实时操作系统中,任务的执行必须在严格的时间约束下进行。WWDG可以确保任务在规定的时间内完成,防止系统因为任务延迟而出现性能问题。
- 电机控制:在电机控制应用中,精确的时间控制对于防止电机过热或损坏非常重要。WWDG可以帮助监控控制循环,确保及时更新电机的控制信号。
- 通信系统:在需要高精度时间同步的通信系统中,如无线基站或网络设备,WWDG可以确保数据包在规定的时间内发送和接收,维护通信的稳定性和可靠性。
总结
-
监控CPU(或微控制器核心):(独立)
- 看门狗可以监控CPU是否能够定期“喂狗”,即更新看门狗的计数器。如果CPU由于硬件故障、电磁干扰、软件错误等原因无法正常工作,它可能无法在规定的时间内更新看门狗,导致看门狗超时并触发系统复位。这样,看门狗确保了即使CPU出现问题,系统也能重启并尝试恢复正常运行。
-
监控程序的执行:(窗口)
- 看门狗也用于确保程序能够按预期运行。在正常情况下,程序会在执行过程中定期喂狗。如果程序由于软件错误、死锁、无限循环或其他原因而无法继续执行,它可能无法及时喂狗,从而导致看门狗超时并重置系统。通过这种方式,看门狗可以帮助检测和恢复由于软件问题导致的系统故障。
实例
独立看门狗
步骤
使用独立看门狗的一般步骤如下:
- 初始化独立看门狗,设置合适的时钟和预分频器。
- 启动独立看门狗。
- 在主循环或其他适当的时机,定期调用
HAL_IWDG_Refresh()
函数刷新看门狗计数器。
函数
常用函数包括:
HAL_IWDG_Init()
- 初始化独立看门狗,设置其时钟和预分频器。HAL_IWDG_Refresh()
- 刷新独立看门狗的计数器,防止看门狗超时导致系统复位。(喂狗)
stm32cude MX
main.h源码
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "iwdg.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_IWDG_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//HAL_Delay(1000);//开启将卡在循环延迟中//超出看门狗的时间
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_IWDG_Refresh(&hiwdg);//喂狗
char a[]={"已喂狗"};
HAL_UART_Transmit(&huart1,(uint8_t*)a,6,20);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
窗口看门狗
步骤如下:
-
配置WWDG时钟: 在使用WWDG之前,需要先使能其时钟。这通常通过调用
__HAL_RCC_WWDG_CLK_ENABLE()
宏来实现。 -
初始化WWDG句柄: 创建一个
WWDG_HandleTypeDef
类型的结构体变量,用于配置WWDG的参数。 -
配置WWDG参数: 在
WWDG_HandleTypeDef
结构体中设置WWDG的参数,包括预分频器(Prescaler)、窗口值(Window)、计数器值(Counter)和早期中断(EWI)模式。 -
初始化WWDG: 调用
HAL_WWDG_Init()
函数,传入步骤2中创建的WWDG句柄作为参数,以初始化WWDG。 -
实现MspInit回调函数(如果需要): 如果需要在WWDG初始化过程中执行特定的硬件配置,可以重写
HAL_WWDG_MspInit()
函数。 -
在主循环中刷新WWDG: 在主循环中,定期调用
HAL_WWDG_Refresh()
函数来刷新WWDG的计数器,以防止WWDG复位。 -
处理错误情况: 如果
HAL_WWDG_Init()
或HAL_WWDG_Refresh()
函数返回错误码(非HAL_OK
),应该有相应的错误处理机制。 -
可选:实现中断回调函数: 如果启用了早期中断(EWI),则需要实现
HAL_WWDG_EarlyWakeupCallback()
函数来处理中断。
窗口看门狗HAL库函数:
- HAL_WWDG_Init: 初始化窗口看门狗,设置预分频器、窗口值和计数器值。
- HAL_WWDG_Refresh: 更新窗口看门狗的计数器,以防止看门狗复位。
- HAL_WWDG_IRQHandler: 窗口看门狗中断处理函数。
- HAL_WWDG_MspInit: 窗口看门狗底层硬件初始化函数。
-
HAL_WWDG_EarlyWakeupCallback:回调函数,它在窗口看门狗的早期唤醒中断发生时被调用
HAL_WWDG_MODULE_ENABLED
是一个宏,用于在HAL库的配置文件stm32fXxx_hal_conf.h
中启用或禁用窗口看门狗模块。
WWDG会触发以下动作:
-
系统复位:WWDG会生成一个系统复位信号,将微控制器重置到其初始状态。这是WWDG的主要功能,用于确保系统在出现软件故障或硬件故障时能够自动恢复。
-
早期中断(EWI):如果在WWDG的配置中启用了早期中断(EWI)功能,那么在计数器接近0之前,WWDG会生成一个中断。这个中断可以用来执行一些清理操作或日志记录,以便在系统复位之前保存重要的信息。
STM32cude MX (要设置一下值和分频值我下面设置的太快了)
在STM32微控制器中,窗口看门狗(WWDG)的时间计算依赖于内部低速时钟(LSI)和预分频器设置。以下是计算WWDG超时时间的基本步骤
源码
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "wwdg.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_WWDG_Init();
/* USER CODE BEGIN 2 */
//HAL_Delay(1000);//
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_WWDG_Refresh(&hwwdg);
char a[]={"已喂狗"};
HAL_UART_Transmit(&huart1,(uint8_t*)a,6,20);
//HAL_WWDG_Refresh(&hwwdg);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */