硬件部分可参考我另一篇文章
接线图
这是课程的相关接线图,独立看门狗和窗口看门狗都适用本图
独立看门狗部分
一、配置流程
1.开启时钟
在数据手册里面有这么一段解释,就是说如果我们开启了看门狗,那么LSI就会跟随强制打开,等待LSI稳定之后就可以自动为独立看门狗提供时钟了。所以这里的第一步开启时钟不需要我们写代码来执行
2.写入预分频器和重装寄存器
在写入这两个寄存器之前需要先写入键值0x555来解除写保护。
3.写入预分频和重装值
可通过超时时间公式进行计算
4.在所有配置进行完之后
可以通过0XCCC这条指令开启独立看门狗,在主循环不断执行0XAAA这条指令进行喂狗
二、代码
1.关于复位时间计算的代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
int main()
{
OLED_Init();
OLED_ShowString(1,1,"IWDG TEST");
if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET)//查看独立看门狗复位的标志位,返回值是set 或 reset
{
OLED_ShowString(2,1,"IWDG RST");
Delay_ms(800);
OLED_ShowString(2,1," ");
Delay_ms(200);
//中断位置1后记得清除标志位
//如果置1后没及时清0,那下次即使是正常的复位键复位也会被判断为看门狗复位
RCC_ClearFlag();
}
else
{
OLED_ShowString(2,1,"NORMOL RST");
Delay_ms(800);
OLED_ShowString(2,1," ");
Delay_ms(200);
}
//接下来需要进行解除写保护
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//这里的enable对应的宏定义是0x5555
//举个例子,设定我们需要的超时时间为1s TIWDG就是1000
//TLSI是1/40KHz,就是0.025ms
//在选择预分频系数的时候,前两个由于最长时间<1000不能选择,所以选择最小能够选择的16分频
//最后用公式计算RL的值 1000/0.025/16-1=2499 !!!注意这个值只能取整数
IWDG_SetPrescaler(IWDG_Prescaler_16);
IWDG_SetReload(2499);
//先喂一次狗,保证启动后的第一个喂狗周期就是1000ms
IWDG_ReloadCounter();
IWDG_Enable();
while(1)
{
IWDG_ReloadCounter();
Delay_ms(1050);//经过测试我的看门狗在1050ms时会复位,在此之前及时喂狗不会产生复位
}
}
2.关于按键阻塞程序运行的程序
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "KEY.h"
int main()
{
OLED_Init();
GPIO_Key_Init();
OLED_ShowString(1,1,"IWDG TEST");
if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET)//查看独立看门狗复位的标志位,返回值是set 或 reset
{
OLED_ShowString(2,1,"IWDG RST");
Delay_ms(800);
OLED_ShowString(2,1," ");
Delay_ms(200);
//中断位置1后记得清除标志位
//如果置1后没及时清0,那下次即使是正常的复位键复位也会被判断为看门狗复位
RCC_ClearFlag();
}
else
{
OLED_ShowString(3,1,"NORMOL RST");
Delay_ms(800);
OLED_ShowString(3,1," ");
Delay_ms(200);
}
//接下来需要进行解除写保护
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//这里的enable对应的宏定义是0x5555
//举个例子,设定我们需要的超时时间为1s TIWDG就是1000
//TLSI是1/40KHz,就是0.025ms
//在选择预分频系数的时候,前两个由于最长时间<1000不能选择,所以选择最小能够选择的16分频
//最后用公式计算RL的值 1000/0.025/16-1=2499 !!!注意这个值只能取整数
IWDG_SetPrescaler(IWDG_Prescaler_16);
IWDG_SetReload(2499);
//先喂一次狗,保证启动后的第一个喂狗周期就是1000ms
IWDG_ReloadCounter();
IWDG_Enable();
while(1)
{
Key_GetNum();//按住按键不放,主循环就会阻塞,则不能及时喂狗,造成复位
IWDG_ReloadCounter();
OLED_ShowString(4,1,"FEED");
Delay_ms(200);
OLED_ShowString(4,1," ");//值得注意的东西,这里不可以使用tab自动空格按键,会造成只有第一个字母闪烁
//需要老老实实敲四下空格
Delay_ms(600);
//Delay_ms(1050);//经过测试我的看门狗在1050ms时会复位,在此之前及时喂狗不会产生复位
}
}
三、总结
· 建议是多留一些时间余量,防止不必要的误差和bug
· !!!
OLED_ShowString(4,1," ");值得注意的东西,这里不可以使用tab自动空格按键,会造成只有第一个字母闪烁,需要老老实实敲四下空格
!!!
窗口看门狗部分
初始化配置
1.开启时钟
由于这里的时钟来源是PCLK1,所以需要手动开启APB1的时钟(与窗口看门狗不同)
2.写入预分频和窗口值
由于窗口看门狗没有写保护,所以可以在第二布直接写入
3.写入控制寄存器CR
控制器CR包含看门狗使能位,计数器溢出标志位和计数器有效位
代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "KEY.h"
int main()
{
OLED_Init();
GPIO_Key_Init();
OLED_ShowString(1,1,"WWDG TEST");
if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET)//查看窗口看门狗复位的标志位,返回值是set 或 reset
{
OLED_ShowString(2,1,"WWDG RST");
Delay_ms(800);
OLED_ShowString(2,1," ");
Delay_ms(200);
//中断位置1后记得清除标志位
//如果置1后没及时清0,那下次即使是正常的复位键复位也会被判断为看门狗复位
RCC_ClearFlag();
}
else
{
OLED_ShowString(3,1,"NORMOL RST");
Delay_ms(800);
OLED_ShowString(3,1," ");
Delay_ms(200);
}
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE); //开启时钟
//假如 设置超时时间为50ms 由计算公式对应的表可以发现 WDGTB只有3对应的超时时间能满足
//预分频系数 = 2^3 = 8 T_WWDG = 50 T_PCLK1 = 1 / 36MHz
//由公式计算得T[5:0] = 54(这个值需要取整)
WWDG_SetPrescaler(WWDG_Prescaler_8);//预分频系数
/*
窗口时间的计算在超时时间计算完成后非常简单 T_WIN设置为30ms
根据公式可计算得 T - W = 33 也就是 54 - W = 33 W = 21
同理,窗口值也需要将第六位置1 同样需要 | 0x40
*/
WWDG_SetWindowValue(0x40 | 21);//窗口值系数
/*
看门狗并没有重装寄存器,是通过直接写入计数器的方式
在使能的时候需要先写入重装值,喂狗的时候也需要不断写入重装值
由上边计算得到的T[5:0]是只有T5-T0的,还有T6位是必须设置为1的
所以需要在54的基础上 | 0x40,(0x40的值来源可参考硬件部分解析)将T6位设置为1
*/
WWDG_Enable(0x40 | 54);
while(1)
{
Key_GetNum();//按住按键不放,主循环就会阻塞,则不能及时喂狗,造成复位
OLED_ShowString(4,1,"FEED");
Delay_ms(20);
OLED_ShowString(4,1," ");
Delay_ms(20);
WWDG_SetCounter(0x40 | 54);
}
}
注意
程序初始化之后,执行到使能看门狗的时候会同时执行第一次喂狗,在接下来进入循环时立刻喂狗将会造成时间间隔小于我们设置的时间间隔30ms,造成喂狗过快造成复位,然后再次执行至此依旧如此,会陷入循环复位卡死。
·建议将循环中的喂狗放至末尾部分,避免喂狗过快这个问题。
·又或者,将使能的代码和喂狗的代码放在一起,使用if判断是否第一次喂狗,第一次喂狗则使用使能顺便喂狗的那个程序,如果是第二次喂狗就执行单独喂狗的程序