文章目录
-
- 一、技术背景与设计思路
-
- [1.1 核心组件功能说明](#1.1 核心组件功能说明)
- [1.2 整体设计流程图](#1.2 整体设计流程图)
- 二、硬件准备与开发环境搭建
-
- [2.1 硬件清单](#2.1 硬件清单)
- [2.2 开发环境配置](#2.2 开发环境配置)
- 三、分步实现:双看门狗+电源监控
-
- [3.1 第一步:STM32CubeMX初始化配置](#3.1 第一步:STM32CubeMX初始化配置)
-
- [3.1.1 新建工程](#3.1.1 新建工程)
- [3.1.2 外设配置](#3.1.2 外设配置)
- [3.1.3 生成代码](#3.1.3 生成代码)
- [3.2 第二步:代码编写与实现](#3.2 第二步:代码编写与实现)
-
- [3.2.1 代码文件结构](#3.2.1 代码文件结构)
- [3.2.2 新建自定义驱动文件](#3.2.2 新建自定义驱动文件)
- [3.2.3 修改main.c文件](#3.2.3 修改main.c文件)
- [3.3 第三步:代码编译与下载](#3.3 第三步:代码编译与下载)
- [3.4 第四步:功能测试与验证](#3.4 第四步:功能测试与验证)
-
- [3.4.1 IWDG测试](#3.4.1 IWDG测试)
- [3.4.2 WWDG测试](#3.4.2 WWDG测试)
- [3.4.3 PVD测试](#3.4.3 PVD测试)
- 四、进阶优化建议
- 五、常见问题与解决
一、技术背景与设计思路
在工业控制、车载电子等对可靠性要求极高的场景中,STM32F7系列单片机的系统稳定性直接决定产品的可用性。单一的硬件保护机制难以应对复杂的现场环境(如电源波动、程序跑飞、电磁干扰等),因此本文设计基于STM32F7的双看门狗(独立看门狗IWDG + 窗口看门狗WWDG)+ 电源监控(PVD) 冗余保护方案,从程序运行监控、电源稳定性检测两个维度提升系统可靠性。
1.1 核心组件功能说明
- 独立看门狗(IWDG):由内部低速时钟(LSI)驱动,不受主时钟故障影响,适用于监控主程序长时间卡死的场景,超时后强制复位系统。
- 窗口看门狗(WWDG):由APB1时钟驱动,具有"窗口"特性,仅在指定时间窗口内喂狗有效,可监控程序是否出现死循环或执行速度异常。
- 电源监控(PVD):通过配置电压检测阈值,实时监控VDD电源电压,当电压低于/高于阈值时触发中断或复位,防止系统在欠压/过压状态下运行导致数据损坏。
1.2 整体设计流程图
是
否
是
否
是
否
系统上电初始化
时钟配置
LSI/APB1/PWR时钟使能
IWDG初始化
设置预分频&重载值
WWDG初始化
设置窗口值&预分频&重载值
PVD初始化
配置电压阈值&中断模式
主程序循环
程序正常执行?
定时喂IWDG
在WWDG窗口内喂狗
IWDG/WWDG超时
系统复位
电源电压正常?
PVD中断触发
执行应急处理
故障恢复?
系统复位
二、硬件准备与开发环境搭建
2.1 硬件清单
- 主控芯片:STM32F767IGT6(兼容其他F7系列)
- 电源模块:5V转3.3V稳压电源(推荐LM1117-3.3)
- 调试工具:ST-Link V2
- 辅助工具:万用表、示波器(可选,用于电源电压检测)
2.2 开发环境配置
- 安装STM32CubeIDE(版本推荐1.15.0及以上):
- 下载地址:https://www.st.com/en/development-tools/stm32cubeide.html
- 安装完成后,配置编译器路径,确保ARM GCC编译器正常加载。
- 安装STM32CubeMX(版本推荐6.9.0及以上):
- 用于图形化配置引脚、时钟、外设等,生成初始化代码。
- 配置ST-Link调试器:
- 连接ST-Link与开发板的SWD接口(SWDIO、SWCLK、GND),确保硬件连接正确。
三、分步实现:双看门狗+电源监控
3.1 第一步:STM32CubeMX初始化配置
3.1.1 新建工程
- 打开STM32CubeMX,点击"New Project",搜索"STM32F767IGT6"并选择对应芯片。
- 配置时钟树:
- 外部晶振(HSE)设为25MHz,系统时钟(SYSCLK)配置为216MHz。
- APB1时钟分频设为4,最终APB1时钟为54MHz(WWDG时钟源为APB1/4096)。
- 启用LSI时钟(32kHz,IWDG时钟源)。
3.1.2 外设配置
(1)独立看门狗(IWDG)配置
- 点击"Configuration" → "IWDG":
- 勾选"IWDG enabled";
- 预分频器(Prescaler)选择"32"(分频系数=32,LSI=32kHz → 计数时钟=1kHz);
- 重载值(Reload Value)设为"1000"(超时时间=1000ms,即1秒喂狗一次);
- 勾选"Enable write access to IWDG_PR and IWDG_RLR registers"。
(2)窗口看门狗(WWDG)配置
- 点击"Configuration" → "WWDG":
- 勾选"WWDG enabled";
- 预分频器(Prescaler)选择"8"(分频系数=8,APB1=54MHz → WWDG时钟=54MHz/4096/8≈1640Hz);
- 窗口值(Window Value)设为"0x70";
- 重载值(Counter Value)设为"0x7F"(超时时间≈(0x7F-0x70)/1640≈5.48ms,窗口时间范围:5.48ms~(0x7F)/1640≈78.6ms);
- 勾选"WWDG early wakeup interrupt"(启用早起唤醒中断,可选)。
(3)电源监控(PVD)配置
- 点击"Configuration" → "Power Configuration":
- 勾选"PVD Level",选择阈值(如"PVD Level 2",对应VDD=2.8V,即电压低于2.8V触发中断);
- 勾选"PVD Mode"为"Interrupt mode"(中断模式,也可选择复位模式);
- 启用PWR时钟(自动配置)。
3.1.3 生成代码
- 点击"Project Manager",设置工程名称(如"STM32F7_WDG_PVD")、保存路径,选择"Toolchain/IDE"为"STM32CubeIDE";
- 点击"GENERATE CODE",生成初始化代码。
3.2 第二步:代码编写与实现
3.2.1 代码文件结构
本次教程核心代码分布在以下文件:
main.c:主程序逻辑、喂狗操作、PVD中断处理stm32f7xx_hal_msp.c:中断优先级配置wdg_pvd.c/wdg_pvd.h:自定义看门狗和PVD驱动函数(新增文件)
3.2.2 新建自定义驱动文件
文件名:wdg_pvd.h
c
#ifndef __WDG_PVD_H
#define __WDG_PVD_H
#include "stm32f7xx_hal.h"
/* 函数声明 */
// IWDG初始化与喂狗
void IWDG_Init_Config(void);
void IWDG_Feed_Dog(void);
// WWDG初始化与喂狗
void WWDG_Init_Config(void);
void WWDG_Feed_Dog(uint8_t counter);
// PVD初始化与中断处理
void PVD_Init_Config(void);
void PVD_Emergency_Handler(void);
#endif /* __WDG_PVD_H */
文件名:wdg_pvd.c
c
#include "wdg_pvd.h"
/* 独立看门狗初始化
* 时钟源:LSI(32kHz),预分频32 → 1kHz计数时钟
* 重载值1000 → 超时时间1000ms
*/
void IWDG_Init_Config(void)
{
// 解锁IWDG寄存器(写0x5555到KR寄存器)
HAL_IWDG_Write(&hiwdg, IWDG_KR, 0x5555);
// 设置预分频器:32
if(HAL_IWDG_PrescalerConfig(&hiwdg, IWDG_PRESCALER_32) != HAL_OK)
{
Error_Handler(); // 初始化失败处理
}
// 设置重载值:1000
if(HAL_IWDG_ReloadConfig(&hiwdg, 1000) != HAL_OK)
{
Error_Handler();
}
// 启动IWDG(写0xCCCC到KR寄存器)
HAL_IWDG_Write(&hiwdg, IWDG_KR, 0xCCCC);
}
/* 独立看门狗喂狗
* 写0xAAAA到KR寄存器,重载计数器
*/
void IWDG_Feed_Dog(void)
{
HAL_IWDG_Write(&hiwdg, IWDG_KR, 0xAAAA);
}
/* 窗口看门狗初始化
* 预分频8,窗口值0x70,计数器初始值0x7F
*/
void WWDG_Init_Config(void)
{
hwwdg.Instance = WWDG;
hwwdg.Init.Prescaler = WWDG_PRESCALER_8; // 预分频8
hwwdg.Init.Window = 0x70; // 窗口值
hwwdg.Init.Counter = 0x7F; // 计数器初始值
hwwdg.Init.EWIMode = WWDG_EWI_ENABLE; // 启用早起唤醒中断
if (HAL_WWDG_Init(&hwwdg) != HAL_OK)
{
Error_Handler();
}
}
/* 窗口看门狗喂狗
* 注意:counter必须大于窗口值且小于0x80,否则喂狗无效
*/
void WWDG_Feed_Dog(uint8_t counter)
{
// 检查计数器值是否在有效范围
if(counter > 0x70 && counter <= 0x7F)
{
HAL_WWDG_SetCounter(&hwwdg, counter);
}
else
{
// 喂狗值无效,触发错误处理
Error_Handler();
}
}
/* PVD电源监控初始化
* 阈值:PVD Level 2 (2.8V),中断模式
*/
void PVD_Init_Config(void)
{
PWR_PVDTypeDef sConfigPVD = {0};
// 启用PWR时钟
__HAL_RCC_PWR_CLK_ENABLE();
// 配置PVD参数
sConfigPVD.PVDLevel = PWR_PVDLEVEL_2; // 2.8V阈值
sConfigPVD.Mode = PWR_PVD_MODE_IT_RISING_FALLING; // 上升/下降沿都触发中断
HAL_PWR_ConfigPVD(&sConfigPVD);
// 启用PVD
HAL_PWR_EnablePVD();
// 配置PVD中断优先级并启用
HAL_NVIC_SetPriority(PVD_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(PVD_IRQn);
}
/* PVD应急处理函数
* 电压异常时执行:保存关键数据、关闭外设、提示故障等
*/
void PVD_Emergency_Handler(void)
{
// 1. 关闭所有外设(如GPIO、UART、SPI等)
__HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_0); // 示例:清除GPIO中断标志
HAL_UART_MspDeInit(&huart1); // 示例:关闭UART1
// 2. 保存关键数据到FLASH(示例:简化版)
// 实际项目中需实现FLASH写入逻辑,此处仅为示例
uint32_t critical_data = 0x12345678;
// FLASH_Program(0x08080000, critical_data); // 需自行实现FLASH编程函数
// 3. 触发故障提示(如点亮LED)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 假设PA5接LED
// 4. 等待100ms后尝试复位(可选)
HAL_Delay(100);
NVIC_SystemReset();
}
3.2.3 修改main.c文件
文件名:main.c
c
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "iwdg.h"
#include "wwdg.h"
#include "wdg_pvd.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* Private user code ---------------------------------------------------------*/
int main(void)
{
/* 1. 初始化HAL库 */
HAL_Init();
/* 2. 配置系统时钟 */
SystemClock_Config();
/* 3. 初始化外设 */
MX_GPIO_Init(); // GPIO初始化(含LED、按键等)
MX_IWDG_Init(); // IWDG底层初始化(CubeMX生成)
MX_WWDG_Init(); // WWDG底层初始化(CubeMX生成)
/* 4. 初始化自定义驱动 */
IWDG_Init_Config(); // 配置IWDG参数并启动
WWDG_Init_Config(); // 配置WWDG参数
PVD_Init_Config(); // 配置PVD电源监控
/* 5. 主循环 */
while (1)
{
/* -------------------------- 喂狗操作 -------------------------- */
// IWDG喂狗:每900ms一次(小于1000ms超时时间)
IWDG_Feed_Dog();
HAL_Delay(900);
// WWDG喂狗:计数器设为0x75(在窗口0x70~0x7F范围内)
WWDG_Feed_Dog(0x75);
/* -------------------------- 业务逻辑 -------------------------- */
// 示例:闪烁LED,指示系统正常运行
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // PA5接LED
HAL_Delay(100);
/* 注:实际项目中,此处添加你的业务代码(如数据采集、控制逻辑等)
* 若业务代码执行时间过长或卡死,看门狗会超时复位
*/
}
}
/* PVD中断服务函数 */
void PVD_IRQHandler(void)
{
// 清除PVD中断标志
HAL_PWR_ClearFlag(PWR_FLAG_PVDO);
// 执行应急处理
PVD_Emergency_Handler();
}
/* 系统时钟配置函数(CubeMX生成,略作注释) */
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置电源电压缩放级别
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
// 配置HSE振荡器
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.LSIState = RCC_LSI_ON; // 启用LSI,供IWDG使用
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 432;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
// 配置系统时钟
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_DIV4; // APB1=54MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
{
Error_Handler();
}
}
/* GPIO初始化函数(示例:配置PA5为输出,接LED) */
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 启用GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置PA5为推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 初始状态:LED熄灭
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
/* 错误处理函数 */
void Error_Handler(void)
{
// 打印错误信息(若启用UART)
// HAL_UART_Transmit(&huart1, (uint8_t*)"Error!", 6, 100);
// 点亮LED,指示错误
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
// 死循环,等待看门狗复位
while(1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
// 断言失败处理
}
#endif /* USE_FULL_ASSERT */
3.3 第三步:代码编译与下载
- 打开STM32CubeIDE,导入生成的工程;
- 将
wdg_pvd.c/wdg_pvd.h添加到工程的Src/Inc目录下; - 点击"Build Project"编译代码,确保无语法错误;
- 连接ST-Link与开发板,点击"Debug"下载代码到STM32F7芯片。
3.4 第四步:功能测试与验证
3.4.1 IWDG测试
- 注释主循环中的
IWDG_Feed_Dog()函数,重新下载代码; - 观察现象:系统运行1秒后自动复位,LED闪烁节奏被打断,证明IWDG生效。
3.4.2 WWDG测试
- 修改
WWDG_Feed_Dog()的参数为0x60(小于窗口值0x70),重新下载代码; - 观察现象:系统快速复位(约5ms),证明WWDG窗口机制生效。
3.4.3 PVD测试
- 使用可调电源给开发板供电,逐步降低VDD电压至2.8V以下;
- 观察现象:PVD中断触发,LED常亮,系统执行应急处理后复位,证明电源监控生效。
四、进阶优化建议
- 看门狗喂狗时机优化:将喂狗操作分散到关键业务逻辑节点,而非固定延时,避免程序跑飞但仍能喂狗的情况。
- PVD阈值分级:根据实际应用场景,配置多级PVD阈值(如2.8V、3.0V、3.2V),实现不同电压等级的差异化处理。
- 故障日志记录:在PVD中断或看门狗复位后,将故障信息写入FLASH,便于后期排查问题。
- 硬件冗余设计:在PCB层面增加电源滤波电容、TVS管等,减少电源波动和电磁干扰对系统的影响。
五、常见问题与解决
- 看门狗频繁复位 :
- 检查喂狗频率是否低于超时时间;
- 排查主程序是否存在长时间阻塞(如死循环、延时过长)。
- PVD中断不触发 :
- 确认PWR时钟已启用;
- 检查PVD中断优先级配置是否正确;
- 验证电压检测阈值是否与实际电源电压匹配。
- LSI时钟不稳定 :
- 在STM32CubeMX中启用LSI校准功能;
- 硬件层面增加滤波电容,减少干扰。
总结
- 本次设计基于STM32F7实现了"独立看门狗+窗口看门狗+电源监控"的三重冗余保护,IWDG监控长周期程序异常,WWDG监控短周期执行异常,PVD监控电源电压波动,全方位提升系统可靠性。
- 核心代码包含完整的初始化、喂狗、中断处理逻辑,零基础用户可按照步骤在STM32CubeIDE中实现落地,关键需注意看门狗喂狗时机和PVD中断优先级配置。
- 测试验证需分别针对三个核心功能进行,通过人为制造异常(如停止喂狗、降低电源电压)验证保护机制是否生效,确保实际场景中系统稳定运行。