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,操作更方便哦


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM