单片机
返回首页

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

2017-02-06 来源:eefocus

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
01137.28
122714.56
245529.12
391058.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 	 
#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->CRL&=0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出
	GPIOA->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->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(&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 , &GPIO_InitStructure); 	 


  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 
	
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;			
  	GPIO_Init(GPIOA , &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 , &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,&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(&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(&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();
}


进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • 光控音效发生器电路

  • 非常简单的150W功放电路图

  • 一个简单的警笛电路图

  • 一个简单的立体声平衡指示器电路

  • 分享一个电网倾角计电路

  • 使用NE555和磁簧开关的橱柜照明电路

    相关电子头条文章