stm32f10x_wwdg

WWDG(Window Watchdog)外设是STM32F10x系列微控制器中的窗口看门狗模块,提供系统监控和自动复位功能。该外设具有窗口功能,只有在特定时间窗口内才能喂狗,防止过早或过晚喂狗,提高系统安全性。

数据类型定义

WWDG预分频器定义

#define WWDG_Prescaler_1    ((uint32_t)0x00000000)  /* 预分频器1 */
#define WWDG_Prescaler_2    ((uint32_t)0x00000080)  /* 预分频器2 */
#define WWDG_Prescaler_4    ((uint32_t)0x00000100)  /* 预分频器4 */
#define WWDG_Prescaler_8    ((uint32_t)0x00000180)  /* 预分频器8 */

说明:

  • WWDG_Prescaler_1:PCLK1时钟1分频,超时时间最短
  • WWDG_Prescaler_2:PCLK1时钟2分频
  • WWDG_Prescaler_4:PCLK1时钟4分频
  • WWDG_Prescaler_8:PCLK1时钟8分频,超时时间最长

WWDG寄存器定义

STM32F10x WWDG外设包含以下主要寄存器:

// WWDG控制寄存器
#define WWDG_CR                     ((uint16_t)0x0000)  /* WWDG控制寄存器 */

// WWDG配置寄存器
#define WWDG_CFR                    ((uint16_t)0x0001)  /* WWDG配置寄存器 */

// WWDG状态寄存器
#define WWDG_SR                     ((uint16_t)0x0002)  /* WWDG状态寄存器 */

标准库函数详解

1. WWDG_DeInit

/**
 * @brief  将 WWDG 寄存器恢复为复位默认值,用于重新配置前清空旧状态
 * @param
 * @retval
 * @example
 *     WWDG_DeInit();
 */
void WWDG_DeInit(void);

功能说明:

  • 复位 CR、CFR 等寄存器到上电默认值
  • 仅在 WWDG 未启动时有效;启动后无法 DeInit
  • 切换看门狗参数前可先调用,再重新配置

2. WWDG_SetPrescaler

/**
 * @brief  设置 WWDG 预分频系数(1/2/4/8),决定计数器递减速率与超时时间
 * @param  WWDG_Prescaler: WWDG预分频器值
 *         该参数可以是以下值之一:
 *           @arg WWDG_Prescaler_1: 预分频器1
 *           @arg WWDG_Prescaler_2: 预分频器2
 *           @arg WWDG_Prescaler_4: 预分频器4
 *           @arg WWDG_Prescaler_8: 预分频器8
 * @retval
 * @example
 *     WWDG_SetPrescaler(WWDG_Prescaler_8);
 */
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);

功能说明:

  • 时钟源为 PCLK1/4096 再经预分频,须在 WWDG_Enable 之前配置
  • 超时 ≈ (Counter+1) × 4096 × Prescaler / PCLK1
  • 分频越大超时越长,典型配置 Prescaler_8

3. WWDG_SetWindowValue

/**
 * @brief  设置窗口看门狗窗口下限值,仅当计数器 ≤ 窗口值时喂狗才有效
 * @param  WindowValue: WWDG窗口值,范围0-0x7F
 * @retval
 * @example
 *     WWDG_SetWindowValue(0x40);
 */
void WWDG_SetWindowValue(uint8_t WindowValue);

功能说明:

  • 窗口值必须小于计数器当前值,否则喂狗会立即触发复位
  • 过早喂狗(计数器 > 窗口值)和超时未喂狗均导致复位
  • 须在 WWDG_Enable 之前设置,是 WWDG 区别于 IWDG 的核心特性

4. WWDG_EnableIT

/**
 * @brief  使能 WWDG 早期唤醒中断,计数器减到 0x40 时产生中断以便紧急处理
 * @param
 * @retval
 * @example
 *     WWDG_EnableIT();
 */
void WWDG_EnableIT(void);

功能说明:

  • 中断在计数器到达 0x40 时触发,距复位尚有时间
  • 须配合 NVIC_EnableIRQ(WWDG_IRQn) 使用
  • 中断中可保存关键数据并调用 WWDG_SetCounter 喂狗

5. WWDG_SetCounter

/**
 * @brief  喂狗:重装载 WWDG 计数器,须在窗口时间内调用才有效
 * @param  Counter: WWDG计数器值,范围0x40-0x7F
 * @retval
 * @example
 *     WWDG_SetCounter(0x7F);
 */
void WWDG_SetCounter(uint8_t Counter);

功能说明:

  • 写入值须 ≥ 0x40,通常设为 0x7F 以获得最长超时
  • 仅当当前计数 ≤ 窗口值时写入有效,否则触发复位
  • 是运行期间唯一需要的喂狗操作

6. WWDG_Enable

/**
 * @brief  启动窗口看门狗并设置计数器初值,启动后无法软件关闭
 * @param  Counter: WWDG计数器值,范围0x40-0x7F
 * @retval
 * @example
 *     WWDG_Enable(0x7F);
 */
void WWDG_Enable(uint8_t Counter);

功能说明:

  • 同时完成启动和计数器初值设置,须在预分频器和窗口值配置后调用
  • 一旦启动仅硬件复位可停止,需先 RCC_APB1PeriphClockCmd 使能时钟
  • Counter 通常取 0x7F,配合窗口值 0x40 形成约一半周期的喂狗窗口

7. WWDG_GetFlagStatus

/**
 * @brief  查询 WWDG 早期唤醒中断标志(EWIF),判断是否即将超时
 * @param
 * @retval 标志状态
 *         该返回值可以是以下值之一:
 *           @arg SET: 标志已设置
 *           @arg RESET: 标志已清除
 * @example
 *     FlagStatus flag = WWDG_GetFlagStatus();
 */
FlagStatus WWDG_GetFlagStatus(void);

功能说明:

  • SET 表示计数器已减至 0x40,即将超时复位
  • 可在主循环轮询,也可在中断服务程序中确认中断源
  • 读取后须调用 WWDG_ClearFlag 清除

8. WWDG_ClearFlag

/**
 * @brief  清除 WWDG 早期唤醒中断标志(EWIF),防止中断重复进入
 * @param
 * @retval
 * @example
 *     WWDG_ClearFlag();
 */
void WWDG_ClearFlag(void);

功能说明:

  • 须在 WWDG_IRQHandler 开头或处理完毕后调用
  • 清除标志后可在中断中执行紧急保存并喂狗
  • 不清除则中断会持续挂起

使用示例

基本WWDG初始化示例

#include "stm32f10x.h"

void WWDG_Init_Example(void)
{
    // 使能WWDG时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
    
    // 设置WWDG预分频器为8
    WWDG_SetPrescaler(WWDG_Prescaler_8);
    
    // 设置WWDG窗口值为0x40
    WWDG_SetWindowValue(0x40);
    
    // 使能WWDG中断
    WWDG_EnableIT();
    
    // 使能NVIC中断
    NVIC_EnableIRQ(WWDG_IRQn);
    
    // 使能WWDG,计数器初始值为0x7F
    WWDG_Enable(0x7F);
}

WWDG超时时间计算示例

#include "stm32f10x.h"

// PCLK1时钟频率(假设为36MHz)
#define PCLK1_FREQ 36000000

// 计算WWDG超时时间(毫秒)
uint32_t WWDG_CalculateTimeout(uint32_t prescaler, uint8_t counter)
{
    uint32_t timeout_ms;
    uint32_t wwdg_freq;
    
    // 计算WWDG时钟频率
    wwdg_freq = PCLK1_FREQ / 4096 / prescaler;
    
    // 计算超时时间
    timeout_ms = (counter + 1) * 1000 / wwdg_freq;
    
    return timeout_ms;
}

void WWDG_TimeoutExample(void)
{
    // 初始化WWDG
    WWDG_Init_Example();
    
    // 计算超时时间
    uint32_t timeout = WWDG_CalculateTimeout(8, 0x7F);
    // timeout约为58.3毫秒
    
    while(1)
    {
        // 定期喂狗,防止系统复位
        WWDG_SetCounter(0x7F);
        
        // 延时一段时间
        for(volatile uint32_t i = 0; i < 100000; i++);
    }
}

WWDG窗口功能示例

#include "stm32f10x.h"

volatile uint8_t wwdg_feed_flag = 0;

void WWDG_WindowExample(void)
{
    // 初始化WWDG
    WWDG_Init_Example();
    
    // 配置定时器中断,定期喂狗
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Period = 49;  // 50ms中断
    TIM_TimeBaseStructure.TIM_Prescaler = 35999;  // 36MHz / 36000 = 1kHz
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    
    // 使能定时器中断
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    
    // 使能NVIC中断
    NVIC_EnableIRQ(TIM2_IRQn);
    
    // 启动定时器
    TIM_Cmd(TIM2, ENABLE);
    
    while(1)
    {
        // 主循环中的其他任务
        if(wwdg_feed_flag)
        {
            // 在窗口时间内喂狗
            WWDG_SetCounter(0x7F);
            wwdg_feed_flag = 0;
        }
    }
}

// TIM2中断服务程序
void TIM2_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {
        // 清除中断标志
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
        
        // 设置喂狗标志
        wwdg_feed_flag = 1;
    }
}

WWDG中断处理示例

#include "stm32f10x.h"

void WWDG_InterruptExample(void)
{
    // 初始化WWDG
    WWDG_Init_Example();
    
    while(1)
    {
        // 主循环中的任务
        // 定期检查WWDG标志
        if(WWDG_GetFlagStatus() == SET)
        {
            // 清除标志
            WWDG_ClearFlag();
            
            // 执行紧急处理
            // 例如:保存重要数据
        }
    }
}

// WWDG中断服务程序
void WWDG_IRQHandler(void)
{
    // 清除中断标志
    WWDG_ClearFlag();
    
    // 执行紧急处理
    // 例如:保存重要数据到备份寄存器
    PWR_BackupAccessCmd(ENABLE);
    BKP_WriteBackupRegister(BKP_DR1, 0x1234);
    
    // 喂狗
    WWDG_SetCounter(0x7F);
}

WWDG安全喂狗示例

#include "stm32f10x.h"

// 安全喂狗函数
void WWDG_SafeFeed(void)
{
    // 检查当前计数器值
    uint8_t current_counter = WWDG_GetCounter();
    
    // 只有在窗口时间内才喂狗
    if(current_counter <= 0x40)  // 窗口值
    {
        WWDG_SetCounter(0x7F);
    }
    else
    {
        // 不在窗口时间内,不喂狗
        // 这会导致系统复位,但保证了安全性
    }
}

void WWDG_SafeExample(void)
{
    // 初始化WWDG
    WWDG_Init_Example();
    
    // 配置定时器中断
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Period = 29;  // 30ms中断
    TIM_TimeBaseStructure.TIM_Prescaler = 35999;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    NVIC_EnableIRQ(TIM2_IRQn);
    TIM_Cmd(TIM2, ENABLE);
    
    while(1)
    {
        // 主循环中的任务
        // 使用安全喂狗函数
        WWDG_SafeFeed();
        
        // 延时
        for(volatile uint32_t i = 0; i < 50000; i++);
    }
}

注意事项

  1. 时钟源:WWDG使用PCLK1时钟,依赖于系统时钟
  2. 窗口功能:只有在窗口时间内喂狗才有效,防止过早或过晚喂狗
  3. 计数器范围:计数器值必须在0x40-0x7F范围内
  4. 窗口值范围:窗口值必须在0-0x7F范围内
  5. 不可禁用:WWDG一旦使能后无法禁用,只能通过系统复位禁用
  6. 中断功能:WWDG支持中断,可以在超时前产生中断
  7. 超时时间:超时时间 = (计数器值 + 1) × 4096 × 预分频器 / PCLK1频率
  8. 调试影响:在调试模式下,WWDG可能影响调试功能

总结

STM32F10x WWDG外设提供了具有窗口功能的看门狗监控,通过窗口机制确保系统在正确的时间窗口内喂狗,提高了系统的安全性。WWDG外设广泛应用于需要高安全性的嵌入式系统中,如工业控制、安全系统等领域。在使用时需要注意正确的初始化顺序、窗口时间设置和安全喂狗机制,确保看门狗功能的正常使用。WWDG的窗口功能是其区别于IWDG的重要特性,能够防止恶意或错误的喂狗操作。