STM32单片机——看门狗(独立看门狗&窗口看门狗)
- 独立看门狗(IWDG)
- 独立看门狗本质相关概念
- 独立看门狗实验
- CubeMX工程配置
- HAL库程序设计
- 固件库程序设计
- 窗口看门狗(WWDG)
- 独立看门狗本质相关概念
- 窗口看门狗实验
- CubeMX工程配置
- HAL库程序设计
- 固件库程序设计
- 独立看门狗与窗口看门狗的异同
- 在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog)
独立看门狗(IWDG)
- 参考博文:外部中断实验&独立看门狗实验
独立看门狗本质相关概念
-
独立看门狗概念
独立看门狗工作在主程序之外,能够完全独立工作,它的时钟是专用的低速时钟(LSI),由VDD 电压供电, 在停止模式和待机模式下仍能工作 -
独立看门狗本质
独立看门狗本质是一个 12 位的递减计数器,当计数器的值从某个值一直减到0的时候,系统就会产生一个复位信号,即 IWDG_RESET 。
如果在计数没减到0之前,刷新了计数器的值的话,那么就不会产生复位信号,这个动作就是我们经常说的喂狗。 -
独立看门狗框图
-
独立看门狗时钟
独立看门狗的时钟由独立的RC振荡器LSI提供,即使主时钟发生故障它仍然有效,非常独立。启用IWDG后,LSI时钟会自动开启。LSI时钟频率并不精确,F1用40kHz。
LSI经过一个8位的预分频器得到计数器时钟。 -
预分频寄存器
分频系数算法
prer是IWDG_PR 的值 -
溢出计算时间公式
独立看门狗实验
- 需求:开启独立看门狗,溢出时间为1秒,使用按键1进行喂狗
CubeMX工程配置
- 配置时钟及时钟树
- 采用外部高速晶振
- 时钟树配置
- 采用外部高速晶振
- 独立开门狗配置
- 启动独立看门狗
- 参数设置(分频系数和重装载值)
- 启动独立看门狗
- 打开串口通信
- 配置异步通信模式
- 串口波特率及参数配置
- 配置异步通信模式
HAL库程序设计
HAL_UART_Transmit(&huart1,(unsigned char *)"hello world\r\n",strlen("hello world\r\n"),200); //while(1) 之前运行,每1秒溢出,重复程序
//按键喂狗 使得程序继续运行
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY1_Pin)
{
HAL_IWDG_Refresh(&hiwdg);
}
}
固件库程序设计
- IWDG独立看门狗操作库函数
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);//取消写保护:0x5555使能 void IWDG_SetPrescaler(uint8_t IWDG_Prescaler); //设置预分频系数:写PR void IWDG_SetReload(uint16_t Reload);//设置重装载值:写RLR void IWDG_ReloadCounter(void);//喂狗: 写0xAAAA到KR void IWDG_Enable(void);//使能看门狗: 写0xCCCC到KR FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG);//状态:重装载/预分频 更新
- 独立看门狗操作步骤
1.取消寄存器写保护:IWDG_WriteAccessCmd();
2.设置独立看门狗的预分频系数,确定时钟:IWDG_SetPrescaler();
3.设置看门狗重装载值,确定溢出时间:IWDG_SetReload();
4.使能看门狗:IWDG_Enable();
5.应用程序喂狗:IWDG_ReloadCounter();
- iwdg.c 独立看门狗程序设计
//初始化独立看门狗 //prer: 分频数:0~7(只有低3位有效!) //分频因子=4*2^prer.但最大值只能是256! //rlr:重装载寄存器值:低11位有效. //时间计算(大概):Tout=((4*2^prer)*rlr)/40 (ms). void IWDG_Init(u8 prer,u16 rlr) { IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//使能对寄存器IWDG_PR和IWDG_RLR的写操作,取消写保护 IWDG_SetPrescaler(prer); //设置IWDG预分频值:设置IWDG预分频值为64 IWDG_SetReload(rlr); //设置IWDG重装载值 IWDG_ReloadCounter(); //按照IWDG重装载寄存器的值重装载IWDG计数器 加载寄存器 IWDG_Enable(); //使能看门狗 } //喂独立看门狗 void IWDG_Feed(void) { IWDG_ReloadCounter(); //重载计数值 }
- 主函数设计
int main(void) { delay_init(); //延时函数初始化 LED_Init(); KEY_Init(); delay_ms(1000); //看得到灭 IWDG_Init(4,625); //预分频数64, 重载值为625, 溢出时间为1s LED1=0; //点亮LED0 while(1) { //按键进行对独立看门狗喂狗 if(KEY_Scan()==KEY1_PRES) { IWDG_Feed(); //如果KEY1按下,则喂狗 } delay_ms(10); } }
窗口看门狗(WWDG)
- 参考博文:窗口看门狗实验&定时器基本原理
独立看门狗本质相关概念
-
什么是窗口看门狗?
窗口看门狗用于监测单片机程序运行时效是否精准,主要检测软件异常,一般用于需要精准检测程序运行时间的场合。
窗口看门狗的本质是一个能产生系统复位信号和提前唤醒中断的6位计数器。
产生复位条件:-
当递减计数器值从 0x40 减到 0x3F 时复位(即T6位跳变到0)
-
计数器的值大于 W[6:0] 值时喂狗会复位。
产生中断条件: 当递减计数器等于 0x40 时可产生提前唤醒中断 (EWI)。
在窗口期内重装载计数器的值,防止复位,也就是所谓的喂狗。 -
-
窗口看门狗工作原理
-
WWDG框图
-
控制寄存器
-
配置寄存器
-
状态寄存器
-
超时时间计算
- Tout是WWDG超时时间(没喂狗)
- Fwwdg是WWDG的时钟源频率(最大36M)
- 4096是WWDG固定的预分频系数
- 2^WDGTB是WWDG_CFR寄存器设置的预分频系数值
- T[5:0]是WWDG计数器低6位,最多63
窗口看门狗实验
- 需求:
开启窗口看门狗,计数器值设置为 0X7F ,窗口值设置为 0X5F ,预分频系数为 8 。程序启动时点亮 LED1 ,500ms 后熄灭。在提前唤醒中断服务函数进行喂狗,同时翻转 LED2 状态。
CubeMX工程配置
- 配置时钟及时钟树
- 采用外部高速晶振
- 时钟树配置
- 采用外部高速晶振
- 窗口开门狗配置
- 开启窗口看门狗及参数设置
- 开启提前唤醒中断
- 开启窗口看门狗及参数设置
- LED灯配置(略)
HAL库程序设计
- main.c主函数设计
int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_RESET); HAL_Delay(500); MX_WWDG_Init(); while (1) { HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET); HAL_Delay(500); } }
- 提前唤醒中断函数喂狗
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg) { HAL_WWDG_Refresh(hwwdg); //喂狗 HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin); }
固件库程序设计
-
窗口看门狗配置过程
1.使能看门狗时钟:RCC_APB1PeriphClockCmd();
2.设置分频系数:WWDG_SetPrescaler();
3.设置窗口值:WWDG_SetWindowValue();
4.开启提前唤醒中断并分组(可选):
WWDG_EnableIT();
NVIC_Init();
5.使能看门狗:
WWDG_Enable();
6.喂狗:WWDG_SetCounter();
7.编写终端服务函数:WWDG_IRQHandler();
-
wwdg.c 窗口看门狗程序设计
//保存WWDG计数器的设置值,默认为最大. u8 WWDG_CNT=0x7f; //初始化窗口看门狗 //tr :T[6:0],计数器值 //wr :W[6:0],窗口值 //fprer:分频系数(WDGTB),仅最低2位有效 //Fwwdg=PCLK1/(4096*2^fprer). void WWDG_Init(u8 tr,u8 wr,u32 fprer) //设置计数器初值、窗口值、预分频系数 { RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); //WWDG时钟使能 WWDG_CNT=tr&WWDG_CNT; //初始化WWDG_CNT. 位运算:与 WWDG_SetPrescaler(fprer); //设置IWDG预分频值 WWDG_SetWindowValue(wr); //设置窗口值 WWDG_Enable(WWDG_CNT); //使能看门狗 , 设置 counter . WWDG_ClearFlag(); //清除提前唤醒中断标志位 WWDG_NVIC_Init(); //初始化窗口看门狗 NVIC WWDG_EnableIT(); //开启窗口看门狗中断 } //重设置WWDG计数器的值 void WWDG_Set_Counter(u8 cnt) //为了和寄存器保持一制,重新包装了一下 { WWDG_Enable(cnt);//使能看门狗 , 设置 counter . } //使能提前唤醒中断 void WWDG_NVIC_Init() { NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //WWDG中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占2,子优先级3,组2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //抢占2,子优先级3,组2 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); //NVIC初始化 }
-
主函数程序初始化
int main(void) { delay_init(); //延时函数初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 LED_Init(); LED1=0; //点亮LED0 delay_ms(1000); WWDG_Init(0X7F,0X5F,WWDG_Prescaler_8);//计数器值为7f,窗口寄存器为5f,分频数为8 while(1) { LED1 = 1; delay_ms(40); } }
-
提前唤醒中断服务函数
void WWDG_IRQHandler(void) //窗口看门狗 中断服务函数 { WWDG_SetCounter(WWDG_CNT); //当禁掉此句后,窗口看门狗将产生复位 WWDG_ClearFlag(); //清除提前唤醒中断标志位 LED1=!LED1; //LED状态翻转 }
独立看门狗与窗口看门狗的异同
对比点 | 独立看门狗 | 窗口看门狗 |
---|---|---|
时钟源 | 独立时钟,LSI (40KHz) ,不精确 | PCLK1或PCLK3,精确 |
复位条件 | 递减计数到0 | 窗口期外喂狗或减到0x3F |
中断 | 没有中断 | 计数值减到0x40可产生中断 |
递减计数器位数 | 12位(最大计数范围:4096 ~ 0) | 7位(最大计数范围:127~63) |
应用场合 | 防止程序跑飞,死循环,死机 | 检测程序时效,防止软件异常 |