STM32F4 窗口看门狗(WWDG)

概述

  为了方便阅读,可以先阅读本人《STM32F407 独立看门狗 (IWDG)》

https://www.cnblogs.com/ding-ding-light/p/14472107.html

  与独立看门狗不同的是,窗口看门狗没有独立的时钟源,它是挂载在APB1下的一个片上外设,与独立看门狗类似,窗口看门狗也需要进行“喂狗”,只不过喂狗的时机被设置成计数器值需要在一个范围之内喂狗,不在这个范围之内喂狗或者低于这个范围的下限系统将发生复位,而这个范围便是上限值和下限值,其中下限值被设定死了是0x40,上限值由用户设置(最大为127)。

寄存器


  以上为窗口看门狗的功能框图,首先来自其中PCLK1时钟被WDG分频器分频后进入窗口看门狗;
  寄存器WWDG_CR为看门狗计数器的值每隔一个时钟周期递减一次;
  寄存器WWDG_CFR保存了预先设置好的上限值,当当前计数器的值T6:0>W6:0时,比较器输出为1,当这个时候进行写寄存器WWDG_CR,接下来的与门会输出1,在输入到或门也将输出1,便会触发复位,前提是寄存器WWDG_CR的WDGA位被置1表示允许窗口看门狗复位发生,这也对应了我之前所说的,当不在上限值和下限值这个范围之内喂狗便会触发复位;
  上图中可以看出另外发生窗口看门狗复位的一种情况是T6为0时,也就是当前计数值已经小于0x40了,前提也是WWDG_CR的WDGA位被置1;
  还有一个寄存器WWDG_CFR的EWI用于选择是否触发中断,当计数器值被减到正好0x40时,可以在中断函数中便可以选择设置一个喂狗的标志位,在主函数中进行喂狗,具体原因独立看门狗已经讲过了,不在赘述。还有一个WDGTB位时用于选择分频器。

窗口看门狗初始化

  通过上面的讲解,我相信初始化过程已经很明了了,有如下步骤:
    1.打开WWDG的硬件时钟;
    2.选择分频器;
    3.设置窗口看门狗的上限值;
    4.配置计数器的值;
    5.NVIC配置;
    6.使能窗口看门狗中断。

实验程序

#include "stm32f4xx.h"
#include <stdio.h>

static GPIO_InitTypeDef 		GPIO_InitStructure;
static NVIC_InitTypeDef   		NVIC_InitStructure;
static USART_InitTypeDef   		USART_InitStructure;

volatile uint8_t		wwdg_event = 0;

#pragma import(__use_no_semihosting_swi)

struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
FILE __stdin;

int fputc(int ch, FILE *f) 
{
	USART_SendData(USART1,ch);
		
	//等待数据发送成功
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
	return ch;
}

void _sys_exit(int return_code) {

}

void delay_us(uint32_t n)
{
	SysTick->CTRL = 0; // Disable SysTick
	SysTick->LOAD = n*168-1; // 计数值 
	SysTick->VAL  = 0; // Clear current value as well as count flag
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock(168MHz)
	while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set
	SysTick->CTRL = 0; // Disable SysTick	
}

void delay_ms(uint32_t n)
{
	while(n--)
	{
		SysTick->CTRL = 0; // Disable SysTick
		SysTick->LOAD = 168000-1; // 计数值 Count from n to 0 (168000-1+1=168000 cycles)
		SysTick->VAL = 0; // Clear current value as well as count flag
		SysTick->CTRL = 5; // Enable SysTick timer with processor clock(168MHz)
		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set
		SysTick->CTRL = 0; // Disable SysTick	
	}
}

void usart1_init(uint32_t baud)
{
	//使能端口A硬件时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	//使能串口1硬件时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	//配置PA9、PA10为复用功能引脚
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9|GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;	
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	//将PA9、PA10连接到USART1的硬件
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9,  GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
	//配置USART1的相关参数:波特率、数据位、校验位
	USART_InitStructure.USART_BaudRate = baud;//波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//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);
	//使能串口1工作
	USART_Cmd(USART1,ENABLE);
}

void usart1_send_str(char *str)
{

	char *p = str;
	
	while(*p!='\0')
	{
		USART_SendData(USART1,*p);
		
		p++;
		//等待数据发送成功
		while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
	}
}

void WWDG_NVICDisable(void)
{
	NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
	NVIC_Init(&NVIC_InitStructure);
}

void WWDG_NVICEnable(void)
{
	NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

void wwdg_init(void)
{
	//打开WWDG的硬件时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
	//配置预分频值
	//窗口看门狗的硬件时钟频率=(42MHz/4096)/8=1281Hz
	//所以窗口看门狗的硬件时钟周期=1/1281
	//到达时间窗口需要:1/1281*(127-80)=37毫秒
	WWDG_SetPrescaler(WWDG_Prescaler_8);
	//窗口的上限值
	WWDG_SetWindowValue(80);
	//配置它的计数值为127看,并使能窗口看门狗工作
	WWDG_Enable(127);
	WWDG_NVICEnable();
	//使能提前唤醒中断
	WWDG_EnableIT();
}

int main(void)
{
	//串口1初始化波特率为115200bps
	usart1_init(115200);
	delay_ms(500);
	//发送数据
	usart1_send_str("This is wwdg test\r\n");
	//检测是否由窗口看门狗导致的复位
	if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET)
	{
		printf("wwdg reset cpu\r\n");
	}
	//清空标志位
	RCC_ClearFlag();
	//窗口看门狗的初始化
	wwdg_init();
	while(1)
	{
		//延时40ms已经到达窗口时间
		delay_ms(40);
		//该变量在中断函数中被改变,在这里需要使用到时,需要关闭中断,使该变量进行临界区
		WWDG_NVICDisable();  
		if(1 == wwdg_event)
		{
			//刷新计数值(将计数值恢复为127) == 喂狗操作
			WWDG_SetCounter(127);	
		}
		WWDG_NVICEnable();
	}
}

void WWDG_IRQHandler(void)
{
	if(WWDG_GetFlagStatus()==SET)
	{
		wwdg_event = 1;
		//清空提前唤醒中断标志位
		WWDG_ClearFlag();
	}
}

总结

  1.注意寄存器WWDG_CFR的中断位在置1后,此中断只有在复位后才由硬件清零,所以要关闭中断时只能在NVIC中关闭;
  2.注意时间的计算;

相关下载

链接:https://pan.baidu.com/s/13qhRg5jOU4eqtZRiMOF9jg 
提取码:czd2 
复制这段内容后打开百度网盘手机App,操作更方便哦
posted @ 2021-03-03 21:34  ding-ding-light  阅读(934)  评论(0编辑  收藏  举报