STM32 IWDG&WWDG
启动看门狗之后,看门狗是不能再被关闭的,除非发生复位。
IWDG独立看门狗
独立看门狗配置流程
-
开启LSI时钟,只有LSI时钟开启了,独立看门狗才能运行。
- 但是开启LSI的代码,并不需要我们来写,因为如果独立看门狗已经由硬件选项或软件启动,LSI振荡器将被强制在打开状态,且不能被关闭。在LSI振荡器稳定后,时钟供应给IWDG。
-
解除IWDG_PR和IWDG_RLR的写保护(向键寄存器写入0x5555)
-
写入预分频器(IWDG_PR)和重装寄存器(IWDG_RLR)。
预分频值和重装值具体写入多少,可以通过超时时间:TIWDG = TLSI × PR预分频系数 × (RL + 1) (其中:TLSI = 1 / FLSI)来计算。
-
启用独立看门狗(向键寄存器写入0xCCCC)
-
在主循环中,不断向键寄存器写入0xAAAA(使IWDG_RLR中的值重新加载到计数器(喂狗)
预分频值和重装值计算
-
TIWDG = TLSI × PR预分频系数 × (RL + 1) (其中:TLSI = 1 / FLSI)
-
比如现在设置超时时间位1000ms,也就是1s,那就是要求,喂狗时间间隔,不能超过1000ms。
-
TIWDG=1000ms,TLSI=1/40KHz=0.025ms。
-
PR和RL都是待定的数值,并且它们的值,可以有多种组合,并不是固定的,随着预分频系数的不同,RL也对应的有不同的选择。
-
目前,我们要设置1000ms,所以前两个预分频是不能选择的(计数器计数最长时间超过了1000ms),剩下的预分频系数都可以选择,因为它们计数的最长时间和最短时间都包含了1000ms。优先选择较小的预分频系数,可以最大化利用计数器的值,来减小时间误差。因为有的时候,得到的RL值是个小数,但RL只能给整数,所以四舍五入取整就会造成误差。
-
这里选择预分频系数为16,计算可得RL=1000/0.025/16-1=2499。(RL范围0~4096)
-
代码示例
main.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"
#include "Key.h"
int main(void)
{
OLED_Init();
Key_Init();
OLED_ShowString(1,1,"IWDG TEST");
if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST)==SET)//查看独立看门狗复位的标志位,判断是否是独立看门狗导致的程序复位
{
OLED_ShowString(2,1,"IWDGSET");
Delay_ms(500);
OLED_ShowString(2,1," ");
Delay_ms(500);
RCC_ClearFlag();//清除标志位
}
else
{
OLED_ShowString(3,1,"RST");
Delay_ms(500);
OLED_ShowString(3,1," ");
Delay_ms(100);
}
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//解除写保护
IWDG_SetPrescaler(IWDG_Prescaler_16);//配置预分频值
IWDG_SetReload(2499);//配置重装值
IWDG_ReloadCounter();//在启动前先喂一次狗,这样启动后的第一个喂狗周期,就是1000ms。
IWDG_Enable();//启动看门狗
//喂狗或使能的时候,会在键寄存器写入0x5555之外的值,又顺便给寄存器写保护了,不用再手动执行写保护。
while(1)
{
Key_GetNum();//按住按键不放,主循环就会阻塞,主循环阻塞,不能及时喂狗,独立看门狗就会复位。
IWDG_ReloadCounter();//喂狗
OLED_ShowString(4,1,"FEED");
Delay_ms(200);
OLED_ShowString(4,1," ");
Delay_ms(600);
}
}
WWDG窗口看门狗
窗口看门狗配置流程
- 开启窗口看门狗APB1的时钟
- 配置预分频系数和窗口值
- 窗口看门狗没有写保护,可以直接写寄存器
- 写入控制寄存器CR
- 看门狗使能位
- 计数器溢出标志位
- 计数器有效位
- 不断向计数器写入想要的重装值,进行喂狗
预分频值和重装值计算
-
超时时间:TWWDG = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] + 1)
-
窗口时间:TWIN = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] - W[5:0])
-
其中:TPCLK1 = 1 / FPCLK1
- 超时时间
- 比如现在设置超时时间是50ms,窗口时间是30ms。
- 首先带入第一个公式,确定预分频和喂狗要给的计数器值,由于想要设置的超时时间为50ms,所以前三个预分频系数是不能选择的(计数器计数最长时间超过了50ms),只能选择最后一个分频系数,只有最后一个分频系数对应的时间范围包含50ms,这里WDGTB=3,对应预分频系数就是23=8。
- TWWDG=50ms TPCLK=1/36M (单位为ms,所以取36000)WDGTB预分频系数=8,计算(取整)可得T+1=55,T[5:0]=54,但这个54只是T[5:0]的值,还有一个T6位也要设置为1,所以再54的基础上,需要再或上0x40,也就是把控制寄存器次高位T6位设置为1。(0x41 | 54)
- 窗口时间
- 预分频系数=8,T=54,TPCLK=1/36M,TWIN=30ms,计算(取整)可得 T-W = 33,W[5:0]=21,同理这个窗口值也是只有W[5:0],低6位,W6这一位,也需要或上0x40,给该位置1。(0x40 | 21)
- 超时时间
代码示例
main.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"
#include "Key.h"
int main(void)
{
OLED_Init();
Key_Init();
OLED_ShowString(1,1,"WWDG TEST");
if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST)==SET)//查看窗口看门狗复位的标志位,判断是否是窗口看门狗导致的程序复位
{
OLED_ShowString(2,1,"WWDGSET");
Delay_ms(500);
OLED_ShowString(2,1," ");
Delay_ms(500);
RCC_ClearFlag();//清除标志位
}
else
{
OLED_ShowString(3,1,"RST");
Delay_ms(500);
OLED_ShowString(3,1," ");
Delay_ms(100);
}
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);//开启WWDG时钟
WWDG_SetPrescaler(WWDG_Prescaler_8);//配置预分频值
WWDG_SetWindowValue(0x40 | 21);//设置窗口值 30ms
WWDG_Enable(0x40 | 54);//开启窗口看门狗,同时喂狗并在窗口看门狗使能位置1 50ms
while(1)
{
Key_GetNum();
OLED_ShowString(4,1,"FEED");
Delay_ms(20);
OLED_ShowString(4,1," ");
Delay_ms(20);
WWDG_SetCounter(0x40 | 54);//先经过40ms的延迟再喂狗(防止立刻复位) 同时在窗口看门狗使能位置0,但是不会关闭窗口看门狗,因为启动看门狗之后,看门狗是不能再被关闭的,除非发生复位
}
}
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"
#include "Key.h"
int main(void)
{
OLED_Init();
Key_Init();
OLED_ShowString(1,1,"WWDG TEST");
if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST)==SET)//查看窗口看门狗复位的标志位,判断是否是窗口看门狗导致的程序复位
{
OLED_ShowString(2,1,"WWDGSET");
Delay_ms(500);
OLED_ShowString(2,1," ");
Delay_ms(500);
RCC_ClearFlag();//清除标志位
}
else
{
OLED_ShowString(3,1,"RST");
Delay_ms(500);
OLED_ShowString(3,1," ");
Delay_ms(100);
}
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);//开启WWDG时钟
WWDG_SetPrescaler(WWDG_Prescaler_8);//配置预分频值
WWDG_SetWindowValue(0x40 | 21);//设置窗口值 30ms
WWDG_Enable(0x40 | 54);//开启窗口看门狗,同时喂狗并在窗口看门狗使能位置1 50ms
while(1)
{
Key_GetNum();
OLED_ShowString(4,1,"FEED");
Delay_ms(20);
OLED_ShowString(4,1," ");
Delay_ms(20);
WWDG_SetCounter(0x40 | 54);//先经过40ms的延迟再喂狗(防止立刻复位) 同时在窗口看门狗使能位置0,但是不会关闭窗口看门狗,因为启动看门狗之后,看门狗是不能再被关闭的,除非发生复位
}
}