以下本人对stm32f4xx的独立看门狗和窗口看门狗学习、理解和总结,程序的说明和解释均在注释中,仔细阅读不难理解。我已经过验证,有问题或错误请指出。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_45003437/article/details/102876242
#include "xwdg.h"
#include "gpio.h"
#include "time.h"
/*=================================================================================================
* 函数名 : Iwdg_Init
* 功能 : 初始化独立看门狗
* 设计者 : rsl
* 日期 : 2019-11-01
* 版本 : V1.0
* 备注 : 时钟范围为 30K~60K ,可用延函数进行测试
===================================================================================================*/
void Iwdg_Init(uint8_t pre ,uint16_t rlr)
{
//使能 预分频寄存器PR和重装载寄存器RLR可写
IWDG->KR = 0X5555;
//设置预分频器值
IWDG->PR = pre;
//设置重装载寄存器值(0~4095)
IWDG->RLR = rlr;
//把重装载寄存器的值放到计数器中
IWDG->KR = 0XAAAA;
//使能计数
IWDG->KR = 0XCCCC;
//喂狗操作
IWDG->KR = 0XAAAA;
}
/*=================================================================================================
* 设置 IWDG 的超时复位时间计算:
* Tout = pre / 40 * rlr (ms)
* 名称 : Iwdg_LibConfig
* 参数 : pre 可以是[0~6] -> [4,8,16,32,64,128,256]
* rlr 重装载寄存器的值,取值范围为:0 ~ 0XFFF(0~4095)
* 设计者 : rsl
* 日期 : 2019-11-02
* 版本 : V1.0
* 备注 : 独立看门狗使用LSI作为时钟, LSI 的频率一般在 30~60KHZ 之间, 根据温度和工作场合会发生一定的
* 漂移,一般取 40KHZ ,所以独立看门狗的定时时间并不一定非常精确, 只适用于对时间精度要求比较低
* 的场合。
* --------------------------------------------
* 调用举例:
* Iwdg_LibConfig(IWDG_Prescaler_64 ,625);
* (64/40)*625 = 1000ms = 1s //IWDG 1s 超时溢出
===================================================================================================*/
void Iwdg_LibConfig(uint8_t pre ,uint16_t rlr)
{
// 使能 预分频寄存器PR和重装载寄存器RLR可写
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
// 设置预分频器值
IWDG_SetPrescaler(pre);
// 设置重装载寄存器值
IWDG_SetReload(rlr);
// 把重装载寄存器的值放到计数器中
IWDG_ReloadCounter();
// 使能 IWDG
IWDG_Enable();
}
/*=================================================================================================
* 函数名 : Iwdg_Feed
* 功能 : 喂独立看门狗
* 设计者 : rsl
* 日期 : 2019-11-01
* 版本 : V1.0
* 备注 : 把重装载寄存器的值放到计数器中(喂狗),防止IWDG复位;当计数器的值减到0的时候会产生系统复位
===================================================================================================*/
void Iwdg_Feed(void)
{
//IWDG->KR = 0XAAAA;
IWDG_ReloadCounter();//喂狗操作
}
/*=================================================================================================
* 函数名 : Iwdg_Test
* 功能 : 独立看门狗测试程序
* 设计者 : rsl
* 日期 : 2019-11-02
* 版本 : V1.0
* 备注 : 因为看门狗初始化程序计数器会消耗一定时间,故看门狗复位指示灯实际闪烁频率比设定的会略慢些
===================================================================================================*/
void Iwdg_Test(uint32_t Nms)
{
//LED 端口初始化
//延时一定时间,此时间是用于控制看门狗复位指示灯(LED1)闪烁频率的
Delay_ms(Nms);
/* 检查是否为 独立看门狗复位 */
if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
{
/* 独立看门狗复位 */
//清除 RCC 的复位标志位
RCC_ClearFlag();
/*如果一直不喂狗,会一直复位,加上前面的延时,会看到LED1灯闪烁;
在 设定的 时间内喂狗的话,则会持续亮LED4灯.*/
LED1_ON;
}
else
{
/* 不是独立看门狗复位(可能为上电复位或者手动按键复位之类的) */
//LED4灯亮
LED4_ON;
}
}
/*=================================================================================================
* WWDG 的超时复位时间计算:
* Twwdg =(4096 × 2^pre ×(T[5:0]+1)) / PCLK1 ; 单位为(ms)
* T[5:0] 窗口看门狗的计数器低 6 位,可取值为0x00~0x3F;(0~63)
* PCLK1 42Mhz --> 42000KHz, 即取42000;
* 函数名 : Wwdg_Init
* 功能 : 初始化窗口看门狗
* 参数 : cnt 表示 T[6:0], 7 位计数器值, 其范围 0x40 ~ 0x7F(64~127)
* win 表示 W[6:0], 7 位窗口值(上窗口), 与递减计数器值进行比较, 其值要小于0x80(0~128)
* pre 表示 WDGTB[1:0] 分频系数(位8:7 -> 00~11)
* 取 WWDG_Prescaler_x ,x 为 1, 2, 4, 8
* 设计者 : rsl
* 日期 : 2019-11-02
* 版本 : V1.0
* 备注 : 窗口看门狗时钟为APB1(PCLK1)时钟总线,42MHz;窗口范围:下窗口固定(0x3F)~上窗口可设(W[6:0])
* --------------------------------------------
* 调用举例:
* Wwdg_Init(0x7F,0x5F,WWDG_Prescaler_8);
* (4096 * 2^8 * (63 + 1)) / 42000kHz = 1198 ms
===================================================================================================*/
void Wwdg_Init(u8 cnt, u8 win, u32 pre)
{
//WWDG 时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
//设置IWDG预分频值
WWDG_SetPrescaler(pre);
//设置窗口值
WWDG_SetWindowValue(win);
//清除提前唤醒中断标志位
WWDG_ClearFlag();
//初始化窗口看门狗 NVIC
WWDG_NVIC_Init();
//开启窗口看门狗中断(递减计数器减到0x40时产生中断)
WWDG_EnableIT();
//设置 7位计数器值并使能看门狗
WWDG_Enable(cnt & 0x7F);
}
/* 配置窗口看门狗中断向量控制器 */
void WWDG_NVIC_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;//WWDG中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);//NVIC初始化
}
/*=================================================================================================
* 函数名 : Wwdge_Feed
* 功能 : 喂窗口看门狗
* 设计者 : rsl
* 日期 : 2019-11-02
* 版本 : V1.0
* 备注 : 输入窗口看门狗定时器值(类似于重装载计数值),低7位有效; 第8位(bit7)为激活位,使能看门狗.
===================================================================================================*/
void Wwdge_Feed(u8 cnt)
{
WWDG_Enable(cnt & 0x7F);//使能看门狗, 重设置WWDG计数器的值
}
/*=================================================================================================
* 函数名 : Wwdg_Test
* 功能 : 窗口看门狗测试程序
* 设计者 : rsl
* 日期 : 2019-11-02
* 版本 : V1.0
* 备注 :
===================================================================================================*/
void Wwdg_Test(uint32_t Nms)
{
//LED 端口初始化
//延时一定时间,此时间是用于控制看门狗复位指示灯(LED1)闪烁频率的
Delay_ms(Nms);
/* 检查是否为 窗口看门狗复位 */
if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST) != RESET)
{
/* 独立看门狗复位 */
//清除 RCC 的复位标志位
RCC_ClearFlag();
/*如果一直不喂狗,会一直复位,加上前面的延时,会看到LED1灯闪烁;
在 设定的 时间内喂狗的话,则会持续亮LED4灯.*/
LED2_ON;
}
else
{
/* 不是独立看门狗复位(可能为上电复位或者手动按键复位之类的) */
//LED4灯亮
LED4_ON;
}
}
/**************************************************************************************************
*独立看门狗应用举例:
int main(void)
{
//初始化按键
Key_GPIO_Config();
//设置 IWDG 500ms 超时溢出
IWDG_Config(IWDG_Prescaler_16 ,1250);
//while部分是我们在项目中具体需要写的代码,这部分的程序可以用独立看门狗来监控;
//如果我们知道这部分代码的执行时间,比如是500ms,那么我们可以设置独立看门狗的
//溢出时间是600ms(有一定误差),比500ms多一点;如果要被监控的程序没有跑飞正常执
//行的话,那么执行完毕之后就会执行喂狗的程序,如果程序跑飞了那程序就会超时,到
//达不了喂狗的程序,此时就会产生系统复位。但是也不排除程序跑飞了又跑回来了,刚
//好喂狗了,歪打正着。所以要想更精确的监控程序,可以使用窗口看门狗,窗口看门狗
//规定必须在规定的窗口时间内喂狗。
while(1)
{
if(Key_Scan() == KEY_DWN)
{
//喂狗,如果不喂狗,系统则会复位,复位后亮红灯,如果在500 ms
//时间内准时喂狗的话,则会亮绿灯
Iwdg_Feed();
//喂狗后亮绿灯
LED_GREEN;
}
}
}
------------------------------------------------------------------------------
*窗口看门狗一般步骤:
1)使能看门狗时钟; 调用函数:RCC_APB1PeriphClockCmd();
2)设置分频系数; 调用函数:WWDG_SetPrescaler();
3)设置上窗口值; 调用函数:WWDG_SetWindowValue();
4)开启提前唤醒中断并分组; 调用函数:WWDG_EnableIT()、NVIC_Init();
5)使能看门狗; 调用函数:WWDG_Enable();
6)喂狗(通常在中断服务中调用); 调用函数:WWDG_SetCounter();
7)编写中断服务函数. 调用函数:WWDG_IRQHandler()。
☆窗口看门狗工作过程总结:
(1)STM32F的窗口看门狗中有一个7位的递减计数器T[6:0],它会在出现下述2种情况之一时产生看门狗复位:
① 当喂狗的时候如果计数器的值大于某一设定数值W[6:0](上窗口值)时;
② 当计数器的数值从 0x40 减到 0x3F 时[由T6位跳变到0]。
(2)在窗口看门狗的递减计数器递减至窗口配置寄存器配置的上窗口(W[6:0])之前,以及在递减计数器递减到下窗口(0x3F)之后
是不能刷新递减计数器的数值(喂狗操作)的; 否则这两种状况都会产生复位信号的。也即喂狗操作只能发生在上、下窗口之间。
注意:如果启动了看门狗并且开启了中断,当在递减计数器递减到0x40时会产生提前唤醒中断(EWI),可以在中断处理函数中
向 WWDG_CR 重新写入计数器值来达到喂狗的目的。
进入中断之后,必须在不长于一个窗口看门狗计数周期的时间(在PCLK1频率为36MHz且WDGTB为0的情况下,该值为 113us)
内重新向WWDG_CR写入计数器值,否则看门狗也将产生复位!
------------------------------------------------------------------------------
参考网资:
独立看门狗
https://blog.csdn.net/weibo1230123/article/details/80705866
窗口看门狗
https://blog.csdn.net/qq_38410730/article/details/79966441