1.WDG简介
独立看门狗(IWDG)由专用的低速时钟(LSI)驱动,即使主时钟发生故障它也仍然有效。窗口看门狗由从APB1时钟分频后得到的时钟驱动,通过可配置的时间窗口来检测应用程序非正常的过迟或过早的操作
2.IWDG框图
- 它的结构和定时器是非常相似的,只不过是定时器溢出产生中断,而看门狗定时器溢出直接产生复位信号,然后喂狗操作其实也就是重置这个计数器,这是一个递减计数器,减到零之后就复位,那程序正常运行时,为了避免复位,就得在这个计数器减到零之前,及时把记数值加大点,这个操作就是喂狗,如果你程序卡死了,没有及时加大这个记数值,那减到零之后就自动复位了,就是看门狗的工作逻辑。
- 预分频器之前输入时钟是LSI内部低速时钟,时钟频率为40KHz,之后时钟进入预分频器进行分频,这个预分频器只有8位,所以它最大只能进行256分频,上面这个预分频寄存器,IWDG_PR可以配置分频系数,这个PR和定时器的PSC是一个意思,他们都是Prescaler的缩写,可能不是一个人设计的,所以这手册里很多缩写都不太一样,不过大家要知道他们其实是一个意思,后面经过预分配器分频之后,时钟驱动递减计数器,每来一个时钟自减一个数,另外这个计数器是12位的,所以最大值是2^12-1=4095,然后当自减到0之后,产生IEDG复位,正常运行时,为了避免复位,我们可以提前在重装寄存器写个值,IWDG_RLR和定时器的ARR是一样的,RLR是reloader,ARR是auto reloader,那当我们预先写好值之后,在运行过程中,我们在这个键寄存器里写个特定数据,控制电路进行喂狗,这时重装值就会复制到当前的计数器中,这样计数器就会回到重装值,重新自减运行了,然后这里有个状态寄存器SR,就是标志电路运行的状态了,其实这个SR里没什么东西,就只有两个更新同步位,基本不用看,最后上面这些寄存器位于1.8V供电区,下面主要的工作电路都位于VDD供电区,所以这下面写了看门狗功能处于vdd供电区,即在停机和待机模式时仍能正常工作,上节我们也说过,独立开门口也是唤醒待机模式的四个条件之一。
3.IWDG键寄存器
- 在键寄存器(IWDG_KR)中写入0xCCCC,开始启用独立看门狗;此时计数器开始从其复位值0xFFF递减计数。当计数器计数到末尾0x000时,会产生一个复位信号(IWDG_RESET)。
- 无论何时,只要在键寄存器IWDG_KR中写入0xAAAA, IWDG_RLR中的值就会被重新加载到计数器,从而避免产生看门狗复位 。
- IWDG_PR和IWDG_RLR寄存器具有写保护功能。要修改这两个寄存器的值,必须先向IWDG_KR寄存器中写入0x5555。以不同的值写入这个寄存器将会打乱操作顺序,寄存器将重新被保护。重装载操作(即写入0xAAAA)也会启动写保护功能。状态寄存器指示预分频值和递减计数器是否正在被更新。
4.IWDG超时时间
FLSI是40KHz 则TLSI是0.025ms TIWDG是自己想要设置的值 再通过该值寻找最长时间里面哪个合适 对应的预分频系数写入 通过公式可以计算出RL(重装值)
5.WWDG框图
- 时钟来源是PCLK1,也就是APB1的时钟,这个时钟默认是36MHz,所以就是36MHz的时钟进来,之后还是先经过一个预分频器进分频,这个和独立看门狗的预分频器,定时器的预分频器都是一个作用,就是灵活的调节后面计数器的时钟频率,同时预分频系数也是计算计数器溢出时间的重要参数,那接着分频之后的时钟驱动这个计数器进行计数,这个计数器和独立看门狗一样,也是一个递减计数器,每来一个时钟自减一次,不过这个计数器比较特殊,从图上来看,这里写了T6到T0,总共是七个位,但下面却写的是六位递减计数器,这是为什么呢,那这其实是因为这个计数器只有T5到T0这六位是有效的就值,最高位T6这里用来当做溢出标志位,第六位等于1时,表示计数器没溢出,T6位等于0时表示计数器溢出,不过对于硬件电路来说,T6位其实也是计数器的一部分,只不过是T6位被单独拎出来,当做标志位了而已。总结一下,就是如果你把T6位看作是计数器的一部分,那要是整个计数器值减到0X40之后移出,而如果你把T6位当成溢出标志位,低6位当做计数器,那就是低6位的计数值减到0之后溢出,这一点尤其要搞清楚。左边的复位(箭头指向复位)信号输出部分,首先这个WDGA是窗口看门狗的激活位,也就是使能,WDGA写入1启用窗口看门狗,使能位作用于这个与门。
- T6位一旦等于零(小圆圈取反),就表示计数器溢出,产生复位信号,那在程序正常运行状态下,我们必须始终保证T6位为一,这样才能避免复位,下面这一块实现的功能和独立看门狗基本是一样的,如果不及时喂狗,6位的计数器减到0后就产生复位。
6.WWDG工作特性
7.WWDG超时时间
这里要多乘一个4096,是因为这里PCLK1进来之后,其实是先执行了一个固定的4096分频,这里框图没画出来,实际上是有的,因为36M的频率还是太快了,先来个固定分频给降一降。
FPCLK1是36MHz 则TPCLK1是1/36
8.IWDG和WWDG对比
9.相关API
9.1 独立看门狗
9.1.1 IWDG_WriteAccessCmd
/**
* @brief Enables or disables write access to IWDG_PR and IWDG_RLR registers.
* @param IWDG_WriteAccess: new state of write access to IWDG_PR and IWDG_RLR registers.
* This parameter can be one of the following values:
* @arg IWDG_WriteAccess_Enable: Enable write access to IWDG_PR and IWDG_RLR registers
* @arg IWDG_WriteAccess_Disable: Disable write access to IWDG_PR and IWDG_RLR registers
* @retval None
*/
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess)
功能:
使能或者失能对寄存器 IWDG_PR 和 IWDG_RLR 的写操作
参数:
IWDG_WriteAccess:对寄存器 IWDG_PR 和 IWDG_RLR 的写操作的新状态
返回值:
无
9.1.2 IWDG_SetPrescaler
/**
* @brief Sets IWDG Prescaler value.
* @param IWDG_Prescaler: specifies the IWDG Prescaler value.
* This parameter can be one of the following values:
* @arg IWDG_Prescaler_4: IWDG prescaler set to 4
* @arg IWDG_Prescaler_8: IWDG prescaler set to 8
* @arg IWDG_Prescaler_16: IWDG prescaler set to 16
* @arg IWDG_Prescaler_32: IWDG prescaler set to 32
* @arg IWDG_Prescaler_64: IWDG prescaler set to 64
* @arg IWDG_Prescaler_128: IWDG prescaler set to 128
* @arg IWDG_Prescaler_256: IWDG prescaler set to 256
* @retval None
*/
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler)
功能:
设置 IWDG 预分频值
参数:
IWDG_Prescaler:IWDG 预分频值
返回值:
无
9.1.3 IWDG_SetReload
/**
* @brief Sets IWDG Reload value.
* @param Reload: specifies the IWDG Reload value.
* This parameter must be a number between 0 and 0x0FFF.
* @retval None
*/
void IWDG_SetReload(uint16_t Reload)
功能:
设置 IWDG 重装载值
参数:
IWDG_Reload:IWDG 重装载值
返回值:
无
9.1.4 IWDG_ReloadCounter
/**
* @brief Reloads IWDG counter with value defined in the reload register
* (write access to IWDG_PR and IWDG_RLR registers disabled).
* @param None
* @retval None
*/
void IWDG_ReloadCounter(void)
功能:
按照 IWDG 重装载寄存器的值重装载 IWDG 计数器
参数:
无
返回值:
无
9.1.5 IWDG_Enable
/**
* @brief Enables IWDG (write access to IWDG_PR and IWDG_RLR registers disabled).
* @param None
* @retval None
*/
void IWDG_Enable(void)
功能:
使能 IWDG
参数:
无
返回值:
无
9.1.6 IWDG_GetFlagStatus
/**
* @brief Checks whether the specified IWDG flag is set or not.
* @param IWDG_FLAG: specifies the flag to check.
* This parameter can be one of the following values:
* @arg IWDG_FLAG_PVU: Prescaler Value Update on going
* @arg IWDG_FLAG_RVU: Reload Value Update on going
* @retval The new state of IWDG_FLAG (SET or RESET).
*/
FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG)
功能:
检查指定的 IWDG 标志位被设置与否
参数:
IWDG_FLAG:待检查的 I2C 标志位
返回值:
IWDG_FLAG 的新状态(SET 或者 RESET)
9.1.7 RCC_GetFlagStatus
/**
* @brief Checks whether the specified RCC flag is set or not.
* @param RCC_FLAG: specifies the flag to check.
*
* For @b STM32_Connectivity_line_devices, this parameter can be one of the
* following values:
* @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready
* @arg RCC_FLAG_HSERDY: HSE oscillator clock ready
* @arg RCC_FLAG_PLLRDY: PLL clock ready
* @arg RCC_FLAG_PLL2RDY: PLL2 clock ready
* @arg RCC_FLAG_PLL3RDY: PLL3 clock ready
* @arg RCC_FLAG_LSERDY: LSE oscillator clock ready
* @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready
* @arg RCC_FLAG_PINRST: Pin reset
* @arg RCC_FLAG_PORRST: POR/PDR reset
* @arg RCC_FLAG_SFTRST: Software reset
* @arg RCC_FLAG_IWDGRST: Independent Watchdog reset
* @arg RCC_FLAG_WWDGRST: Window Watchdog reset
* @arg RCC_FLAG_LPWRRST: Low Power reset
*
* For @b other_STM32_devices, this parameter can be one of the following values:
* @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready
* @arg RCC_FLAG_HSERDY: HSE oscillator clock ready
* @arg RCC_FLAG_PLLRDY: PLL clock ready
* @arg RCC_FLAG_LSERDY: LSE oscillator clock ready
* @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready
* @arg RCC_FLAG_PINRST: Pin reset
* @arg RCC_FLAG_PORRST: POR/PDR reset
* @arg RCC_FLAG_SFTRST: Software reset
* @arg RCC_FLAG_IWDGRST: Independent Watchdog reset
* @arg RCC_FLAG_WWDGRST: Window Watchdog reset
* @arg RCC_FLAG_LPWRRST: Low Power reset
*
* @retval The new state of RCC_FLAG (SET or RESET).
*/
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG)
功能:
检查指定的 RCC 标志位设置与否
参数:
RCC_FLAG:待检查的 RCC 标志位
返回值:
RCC_FLAG 的新状态(SET 或者 RESET)
9.2 窗口看门狗
9.2.1 WWDG_SetPrescaler
/**
* @brief Sets the WWDG Prescaler.
* @param WWDG_Prescaler: specifies the WWDG Prescaler.
* This parameter can be one of the following values:
* @arg WWDG_Prescaler_1: WWDG counter clock = (PCLK1/4096)/1
* @arg WWDG_Prescaler_2: WWDG counter clock = (PCLK1/4096)/2
* @arg WWDG_Prescaler_4: WWDG counter clock = (PCLK1/4096)/4
* @arg WWDG_Prescaler_8: WWDG counter clock = (PCLK1/4096)/8
* @retval None
*/
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler)
功能:
设置 WWDG 预分频值值
参数:
WWDG_Prescaler:指定 WWDG 预分频
返回值:
无
9.2.2 WWDG_SetWindowValue
/**
* @brief Sets the WWDG window value.
* @param WindowValue: specifies the window value to be compared to the downcounter.
* This parameter value must be lower than 0x80.
* @retval None
*/
void WWDG_SetWindowValue(uint8_t WindowValue)
功能:
设置 WWDG 窗口值
参数:
WindowValue r:指定的窗口值。该参数取值必须在 0x40 与 0x7F 之间。
返回值:
无
9.2.3 WWDG_SetCounter
/**
* @brief Sets the WWDG counter value.
* @param Counter: specifies the watchdog counter value.
* This parameter must be a number between 0x40 and 0x7F.
* @retval None
*/
void WWDG_SetCounter(uint8_t Counter)
功能:
设置 WWDG 计数器值
参数:
Counter:指定看门狗计数器值。该参数取值必须在 0x40 与 0x7F 之间。
返回值:
无
9.2.4 WWDG_Enable
/**
* @brief Enables WWDG and load the counter value.
* @param Counter: specifies the watchdog counter value.
* This parameter must be a number between 0x40 and 0x7F.
* @retval None
*/
void WWDG_Enable(uint8_t Counter)
功能:
使能 WWDG 并装入计数器值
参数:
Counter:指定看门狗计数器值。该参数取值必须在 0x40 与 0x7F 之间。
返回值:
无
9.2.5 WWDG_GetFlagStatus
/**
* @brief Checks whether the Early Wakeup interrupt flag is set or not.
* @param None
* @retval The new state of the Early Wakeup interrupt flag (SET or RESET)
*/
FlagStatus WWDG_GetFlagStatus(void)
功能:
检查 WWDG 早期唤醒中断标志位被设置与否
参数:
无
返回值:
早期唤醒中断标志位的新状态(SET 或者 RESET)
9.2.6 WWDG_ClearFlag
/**
* @brief Clears Early Wakeup interrupt flag.
* @param None
* @retval None
*/
void WWDG_ClearFlag(void)
功能:
清除早期唤醒中断标志位
参数:
无
返回值:
无
10.独立看门狗
10.1 接线图
10.2 相关代码
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Key_Init(); //按键初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "IWDG TEST");
/*判断复位信号来源*/
if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET) //如果是独立看门狗复位
{
OLED_ShowString(2, 1, "IWDGRST"); //OLED闪烁IWDGRST字符串
Delay_ms(500);
OLED_ShowString(2, 1, " ");
Delay_ms(100);
RCC_ClearFlag(); //清除标志位 若不清除标志位 则即使按下复位键 也不会自动清0 则会产生看门狗复位 就算下次是正常的复位键复位 也会判断为看门狗复位
}
else //否则,即为其他复位
{
OLED_ShowString(3, 1, "RST"); //OLED闪烁RST字符串
Delay_ms(500);
OLED_ShowString(3, 1, " ");
Delay_ms(100);
}
/*IWDG初始化*/
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //独立看门狗写使能
IWDG_SetPrescaler(IWDG_Prescaler_16); //设置预分频为16
IWDG_SetReload(2499); //设置重装值为2499,独立看门狗的超时时间为1000ms
IWDG_ReloadCounter(); //重装计数器,喂狗,这样子之后第一行周期就是1000ms,因为喂完一次狗 计数器会从1000开始自减
IWDG_Enable(); //独立看门狗使能
while (1)
{
Key_GetNum(); //调用阻塞式的按键扫描函数,模拟主循环卡死,即程序不会执行下面代码,则不会喂狗,则会产生独立看门狗复位
IWDG_ReloadCounter(); //重装计数器,喂狗,每800ms喂一次狗
OLED_ShowString(4, 1, "FEED"); //OLED闪烁FEED字符串
Delay_ms(200); //喂狗间隔为200+600=800ms
OLED_ShowString(4, 1, " ");
Delay_ms(600);
}
}
现象:上电之后先喂一次狗 后续计数值为1000 进入循环后每隔800ms喂一次狗 oled屏闪烁FEED字样 若按下复位键复位 则会出现RST字样 若按下独立按键不放 则不会800ms喂一次狗 触发看门狗复位 oled屏出现IDWRST字样
11.窗口看门狗
11.1 接线图
11.2 相关代码
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Key_Init(); //按键初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "WWDG TEST");
/*判断复位信号来源*/
if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET) //如果是窗口看门狗复位
{
OLED_ShowString(2, 1, "WWDGRST"); //OLED闪烁WWDGRST字符串
Delay_ms(500);
OLED_ShowString(2, 1, " ");
Delay_ms(100);
RCC_ClearFlag(); //清除标志位
}
else //否则,即为其他复位
{
OLED_ShowString(3, 1, "RST"); //OLED闪烁RST字符串
Delay_ms(500);
OLED_ShowString(3, 1, " ");
Delay_ms(100);
}
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); //开启WWDG的时钟
/*WWDG初始化*/
WWDG_SetPrescaler(WWDG_Prescaler_8); //设置预分频为8
WWDG_SetWindowValue(0x40 | 21); //设置窗口值,窗口时间为30ms,|0x40(0100 0000)是让T6位置1 才能参与与门打开复位
WWDG_Enable(0x40 | 54); //使能并第一次喂狗,超时时间为50ms,|0x40(0100 0000)是让T6位置1 才能参与与门打开复位
while (1)
{
Key_GetNum(); //调用阻塞式的按键扫描函数,模拟主循环卡死
// WWDG_SetCounter(0x40 | 54); //重装计数器,喂狗
//不放在这里而放在delay函数后面的原因是:上面定义30-50ms内不会触发喂狗 若在这里喂狗 则时间可能在30ms之前 会触发看门狗复位 所以需要在闪烁字符串后喂狗 则在30-50之间
OLED_ShowString(4, 1, "FEED"); //OLED闪烁FEED字符串
Delay_ms(20); //喂狗间隔为20+20=40ms
OLED_ShowString(4, 1, " ");
Delay_ms(20);
WWDG_SetCounter(0x40 | 54); //重装计数器,喂狗
}
}
现象:接上电源后喂狗一次 设置窗口时间和超时时间分别是30ms和50ms 在此范围内不发生看门狗复位 在此范围外则发生看门狗复位 函数进入主循环 若按键没按下则OLED显示FEED字样不断闪烁 后面喂狗一次则在30-50之间 不会产生看门狗复位 按下按键 程序阻塞无法执行下面延时 则相当于超时喂狗 会产生看门狗复位 若要观察过快喂狗现象 则可以将一个Delay注释掉 让另一个Delay的值小于或等于30 则每次喂狗在30ms之前 会产生看门狗复位