概述
為了方便閱讀,可以先閱讀本人《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,操作更方便哦