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