stm32 窗口看门狗[操作寄存器+库函数]

stm32有两个看门狗,独立看门狗和窗口看门狗,其实两者的功能是类似的,只是喂狗的限制时间不同。
  • 独立看门狗是限制喂狗时间在0-x内,x由你的相关寄存器决定。喂狗的时间不能过晚。
  • 窗口看门狗,所以称之为窗口就是因为其喂狗时间是一个有上下限的范围内,你可以通过设定相关寄存器,设定其上限时间和下限时间。喂狗的时间不能过早也不能过晚。
    窗口看门狗的上窗口就是配置寄存器WWDG->CFR里设定的W[6:0],下窗口是固定的0x40;当窗口看门狗的计数器在上窗口值之外,或是低于下窗口值都会产生复位。
    上窗口的值可以只有设定,7位二进制数最大只可以设定为127,最小又必须大于下窗口的0x40,所以其取值范围为40~127; 
    窗口看门狗的核心是一个7位的递减计数器,只有当计数器的值在窗口范围内在可以执行喂狗操作,即更新此计数器的值,否则将导致复位操作。 在配置寄存器WWDG->CFR中可以为此计数器设定时钟分频系数,确定这个计数器可以定时的时间范围,从而确定窗口的时间范围。
    窗口看门狗的时钟来自于PCLK1,在时钟配置中,其频率为外部时钟经倍频器后的二分频时钟,即为36MHz,根据手册可以知道其定时时间计算方法:
           上窗口时间 T_min = 4096 (2^WDGTB)(WWDG_CR[6:0] - WWDG_CFR[6:0])/36 (us)
           下窗口时间 T_max = 4096 (2^WDGTB)(WWDG_CR[6:0] - 0x40)/36 (us)

36Mhz下相关窗口看门狗的喂狗时间范围:

WDGTB(计数器分频值) 最早喂狗时间/us 最晚喂狗时间/ms
0 113 7.28
1 227 14.56
2 455 29.12
3 910 58.25
本例直接寄存器实现4种测试模式,测试窗口看门狗的复位原理。

  • MODE_1 0   // 在30ms时喂狗,在窗口范围内喂狗,LED灯闪烁
  • MODE_2 0   // 在10ms时喂狗,在窗口范围外导致复位 ,LED灯常亮
  • MODE_3 0   // 在100ms时喂狗,在窗口范围外喂狗导致复位
  • MODE_4 1   // 主函数不执行喂狗,开启提前唤醒中断,在WWDG中断函数中喂狗

    库函数实现在提前唤醒中断中,喂狗操作,PA4口LED正常闪烁。当外部中断发生(按下PA0按键),长时间不喂狗,引发窗口看门狗复位。
    直接操作寄存器
    相关寄存器如下:
    控制寄存器WWDG->CR: 低8位有效。第8位为WDGA,看门狗激活位。低7位[6:0]为看门狗计数器的计数值。
    配置寄存器WWDG->CFR:
    低十位有效。
    第10位为EWI:提醒唤起中断。 此位置1,当计数器值到达0x40时,将产生中断。
    第8,9位WDGTB:为计数器分频系数设置为:

  • 00: CK计时器时钟(PCLK1除以4096)除以1     01: CK计时器时钟(PCLK1除以4096)除以2

  • 10: CK计时器时钟(PCLK1除以4096)除以4     11: CK计时器时钟(PCLK1除以4096)除以8
    低7位[6:0]:为窗口值。
    代码如下:    (system.h 和 stm32f10x_it.h 等相关代码参照 stm32 直接操作寄存器开发环境配置
    User/main.c
    #include <stm32f10x_lib.h>
    #include “system.h”
    #include “wdg.h”

#define LED1 PAout(4)

//选择测试模式,一次置1一个测试

#define MODE_1 0 // 在30ms时喂狗,在窗口范围内喂狗

#define MODE_2 0 // 在10ms时喂狗,在窗口范围外导致复位

#define MODE_3 0 // 在100ms时喂狗,在窗口范围外喂狗导致复位

#define MODE_4 1 // 主函数不执行喂狗,开启提前唤醒中断,在WWDG中断函数中喂狗

void Gpio_Init(void);

int main(void)
{

Rcc_Init(9);              //系统时钟设置

Wwdg_Init(127,100,3);  //设定为只能在24.57ms~58.25ms内喂狗,时间计算方法参见wdg.c Wwdg_Init();

#if MODE_4
    Nvic_Init(0,0,WWDG_IRQChannel,0);      //设置抢占优先级为0,响应优先级为0,中断分组为0
#endif

Gpio_Init();

while(1){

    LED1 = !LED1;

    #if MODE_1
        delay(30000);      //延时30ms后喂狗,LED闪烁
        Wwdg_Feed();    //喂狗
    #elif MODE_2
        delay(10000);      //延时10ms,引发窗口看门狗复位,LED不闪烁
        Wwdg_Feed();    //喂狗
    #elif MODE_3
        delay(100000);      //延时100ms,引发窗口看门狗复位,LED不闪烁
        Wwdg_Feed();    //喂狗
    #elif MODE_4
        delay(30000);      //延时30ms后在中断中喂狗,LED闪烁
    #endif

}        

}

void Gpio_Init(void)
{
RCC->APB2ENR|=1<<2; //使能PORTA时钟

GPIOA-&gt;CRL&amp;=0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出
GPIOA-&gt;CRL|=0x33334444; 

}

User/stm32f10x_it.c
#include “stm32f10x_it.h”

extern void Wwdg_Feed(void);

void WWDG_IRQHandler(void)
{
//必须判断 计数值小于 窗口值之后才可以喂狗,不加此判断直接喂狗会导致复位,原因不详
if((WWDG->CR&0x7F) < (WWDG -> CFR&0x7F)){
Wwdg_Feed();
}

WWDG-&gt;SR = 0x00;  //清除中断标志位

}

Library/src/wdg.c 和  Library/inc/wdg.h 参见 stm32 独立看门狗[操作寄存器+库函数]
在中断函数 stm32f10x_it.c WWDG_IRQChannel()中 必须判断 计数值小于 窗口值之后才可以喂狗,不加此判断直接喂狗会导致复位,原因不详。
此中断是在计数器计数到0x40时触发,必然会小于窗口设定值 ,除非是此中断不止在此条件下触发,启动WWDG计数器可能也会导致中断。猜测一下而已 。。。
库函数操作
main.c
#include “stm32f10x.h”

#include “stdio.h”

#define PRINTF_ON 1

void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void USART_Configuration(void);
void WWDG_Configuration(void);
void EXTI_Configuration(void);

int main(void)
{
RCC_Configuration();
GPIO_Configuration();
NVIC_Configuration();
USART_Configuration();
EXTI_Configuration();
WWDG_Configuration();

while(1){    
    if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST) != RESET)
    {
        printf("\r\n The Stm32 has been reset by WWDG .\r\n");
        RCC_ClearFlag();
    }
}

}

void EXTI_Configuration(void)
{
EXTI_InitTypeDef EXTI_InitStructure;

EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&amp;EXTI_InitStructure);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);

}

void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA , &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;            
GPIO_Init(GPIOA , &amp;GPIO_InitStructure);      

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;            
GPIO_Init(GPIOA , &amp;GPIO_InitStructure); 

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;            
GPIO_Init(GPIOA , &amp;GPIO_InitStructure); 

}

void WWDG_Configuration(void)
{
WWDG_SetPrescaler(WWDG_Prescaler_8);
WWDG_Enable(0x7F);
WWDG_ClearFlag();
WWDG_EnableIT();
}

void RCC_Configuration(void)
{
/ 定义枚举类型变量 HSEStartUpStatus /
ErrorStatus HSEStartUpStatus;

  /* 复位系统时钟设置*/
  RCC_DeInit();
  /* 开启HSE*/
  RCC_HSEConfig(RCC_HSE_ON);
  /* 等待HSE起振并稳定*/
  HSEStartUpStatus = RCC_WaitForHSEStartUp();
/* 判断HSE起是否振成功,是则进入if()内部 */
  if(HSEStartUpStatus == SUCCESS)
  {
    /* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
    RCC_HCLKConfig(RCC_SYSCLK_Div1); 
    /* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
    RCC_PCLK2Config(RCC_HCLK_Div1); 
    /* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
    RCC_PCLK1Config(RCC_HCLK_Div2);
    /* 设置FLASH延时周期数为2 */
    FLASH_SetLatency(FLASH_Latency_2);
    /* 使能FLASH预取缓存 */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    /* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
    /* 使能PLL */ 
    RCC_PLLCmd(ENABLE);
    /* 等待PLL输出稳定 */
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
    /* 选择SYSCLK时钟源为PLL */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    /* 等待PLL成为SYSCLK时钟源 */
    while(RCC_GetSYSCLKSource() != 0x08);
  } 
  /* 打开APB2总线上的GPIOA时钟*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP|RCC_APB1Periph_WWDG, ENABLE);

}

void USART_Configuration(void)
{
USART_InitTypeDef USART_InitStructure;
USART_ClockInitTypeDef USART_ClockInitStructure;

USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;                                                                                                                                                      
USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
USART_ClockInit(USART1 , &amp;USART_ClockInitStructure);

USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
USART_Init(USART1,&amp;USART_InitStructure);

 USART_Cmd(USART1,ENABLE);

}

void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&amp;NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&amp;NVIC_InitStructure);

}

#if PRINTF_ON

int fputc(int ch,FILE *f)
{
USART_SendData(USART1,(u8) ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
return ch;
}

#endif

stm32f10x_it.c
#include “stm32f10x_it.h”

#include “stdio.h”

extern vu32 Display;

void WWDG_IRQHandler(void)
{
WWDG_SetCounter(0x7F);
GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4)));
WWDG_ClearFlag();
}