独立看门狗是一个 12 位的递减计数器,当计数器的值从某个值一直减到 0 的时候,系统就会产生一个复位信号,即 IWDG_RESET。如果在计数没减到 0 之前,刷新了计数器的值的话,那么就不会产生复位信号,这个动作就是我们经常说的喂狗。看门狗功能由?VDD 电压域供电,在停止模式和待机模式下仍能工作。
独立看门狗的时钟由独立的 RC 振荡器 LSI 提供,即使主时钟发生故障它仍然有效,非常独立。LSI 的频率一般在 30~60KHZ 之间,根据温度和工作场合会有一定的漂移,我们一般取 40KHZ,所以独立看门狗的定时时间并不一定非常精确,只适用于对时间精度要求比较低的场合。
递减计数器的时钟由 LSI 经过一个 8 位的预分频器得到,我们可以操作预分频器寄存器IWDG_PR 来设置分频因子,分频因子可以是:[4,8,16,32,64,128,256,256]。?
计数器时钟 CK_CNT= 40/ 4*2^PRV。
独立看门狗的计数器是一个 12 位的递减计数器,最大值为 0XFFF,当计数器减到 0 时,会产生一个复位信号:IWDG_RESET,让程序重新启动运行,如果在计数器减到 0 之前刷新了计数器的值的话,就不会产生复位信号,重新刷新计数器值的这个动作我们俗称喂狗。
重装载寄存器是一个 12 位的寄存器,里面装着要刷新到计数器的值,这个值的大小决定着独立看门狗的溢出时间。
超时时间 Tout = (4*2^prv) / 40 * rlv (s) 。
prv 是预分频器寄存器的值,rlv 是重装载寄存器的值。
键寄存器 IWDG_KR 可以说是独立看门狗的一个控制寄存器。
通过写往键寄存器写 0XCCCC?来启动看门狗是属于软件启动的方式,一旦独立看门狗启动,它就关不掉,只有复位才能关掉。?
独立看门狗一般用来检测和解决由程序引起的故障,比如一个程序正常运行的时间是 50ms,在运行完这个段程序之后紧接着进行喂狗,我们设置独立看门狗的定时溢出时间为 60ms,比我们需要监控的程序 50ms 多一点,如果超过 60ms 还没有喂狗,那就说明我们监控的程序出故障了,跑飞了,那么就会产生系统复位,让程序重新运行。
实验目的:若1s内未能喂狗,亮红灯,及时喂狗了亮绿灯。
systick.c
#include "systick.h"
void SysTick_Delay_us(uint32_t us)
{
uint32_t i;
SysTick_Config(72);
for(i=0; i<us; i++)
{
while( !((SysTick->CTRL) & (1<<16)) );
}
SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}
void SysTick_Delay_ms(uint32_t ms)
{
uint32_t i;
SysTick_Config(72000);
for(i=0; i<ms; i++)
{
while( !((SysTick->CTRL) & (1<<16)) );
}
SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}
led.c
#include "led.h"
void led_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(LED0_GPIO_CLK,ENABLE);//注意需要用到的总线
GPIO_InitStruct.GPIO_Pin = LED0_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED0_GPIO_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LED1_GPIO_PIN;
GPIO_Init(LED1_GPIO_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LED2_GPIO_PIN;
GPIO_Init(LED2_GPIO_PORT,&GPIO_InitStruct);
GPIO_ResetBits(LED0_GPIO_PORT,LED0_GPIO_PIN);
GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);
GPIO_ResetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);
}
led.h
#ifndef _LED_H
#define _LED_H
#include "stm32f10x.h"
#define LED0_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED0_GPIO_PORT GPIOB
#define LED0_GPIO_PIN GPIO_Pin_5
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED1_GPIO_PORT GPIOB
#define LED1_GPIO_PIN GPIO_Pin_6
#define LED2_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED2_GPIO_PORT GPIOB
#define LED2_GPIO_PIN GPIO_Pin_7
/******************************************************************************************/
/* LED端口定义 */
#define LED0(x) do{ x ? \
GPIO_SetBits(LED0_GPIO_PORT, LED0_GPIO_PIN) : \
GPIO_ResetBits(LED0_GPIO_PORT, LED0_GPIO_PIN); \
}while(0) /* LED0翻转 */
#define LED1(x) do{ x ? \
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN) : \
GPIO_ResetBits(LED1_GPIO_PORT, LED1_GPIO_PIN); \
}while(0) /* LED0翻转 */
#define LED2(x) do{ x ? \
GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN) : \
GPIO_ResetBits(LED2_GPIO_PORT, LED2_GPIO_PIN); \
}while(0) /* LED0翻转 */
#define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
#define LED0_Toggle digitalToggle(LED0_GPIO_PORT,LED0_GPIO_PIN)
#define LED1_Toggle digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED2_Toggle digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
void led_gpio_init(void);
#endif
key.c
#include "key.h"
/**
* @brief 配置按键用到的I/O口
* @param 无
* @retval 无
*/
void Key_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE);
GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN|KEY2_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY1_GPIO_PORT,&GPIO_InitStructure);
GPIO_Init(KEY2_GPIO_PORT,&GPIO_InitStructure);
}
/*
* 函数名:Key_Scan
* 描述 :检测是否有按键按下
* 输入 :GPIOx:x 可以是 A,B,C,D或者 E
* GPIO_Pin:待读取的端口位
* 输出 :KEY_OFF(没按下按键)、KEY_ON(按下按键)
*/
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
/*检测是否有按键按下 */
if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON )
{
/*等待按键释放 */
while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);
return KEY_ON;
}
else
return KEY_OFF;
}
key.h
#ifndef _KEY_H
#define _KEY_H
#include "stm32f10x.h"
// 引脚定义
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
#define KEY1_GPIO_PORT GPIOA
#define KEY1_GPIO_PIN GPIO_Pin_0
#define KEY2_GPIO_CLK RCC_APB2Periph_GPIOA
#define KEY2_GPIO_PORT GPIOA
#define KEY2_GPIO_PIN GPIO_Pin_1
/** 按键按下标置宏
* 按键按下为高电平,设置 KEY_ON=1, KEY_OFF=0
* 若按键按下为低电平,把宏设置成KEY_ON=0 ,KEY_OFF=1 即可
*/
#define KEY_ON 1
#define KEY_OFF 0
void Key_GPIO_Config(void);
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
#endif
iwdg.c
#include "iwdg.h"
/*
* 设置 IWDG 的超时时间
* Tout = prv/40 * rlv (s)
* prv可以是[4,8,16,32,64,128,256]
* prv:预分频器值,取值如下:
* @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
*
* rlv:重装载寄存器的值,取值范围为:0-0XFFF
* 函数调用举例:
* IWDG_Config(IWDG_Prescaler_64 ,625); // IWDG 1s 超时溢出
* 溢出时间 Tout = prv/40 * rlv(s) Tout=64/40*625=1s
*/
/**
* @brief 配置IWDG参数
* @param PRV:预分频器值
RLV:重装载寄存器值
* @retval 无
*/
void IWDG_Config(uint8_t prv ,uint16_t rlv)
{
// 使能 预分频寄存器PR和重装载寄存器RLR可写
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
// 设置预分频器值
IWDG_SetPrescaler( prv );
// 设置重装载寄存器值
IWDG_SetReload( rlv );
// 把重装载寄存器的值放到计数器中
IWDG_ReloadCounter();
// 使能 IWDG
IWDG_Enable();
}
/**
* @brief 喂狗
* @param 无
* @retval 无
*/
void IWDG_Feed(void)
{
// 把重装载寄存器的值放到计数器中,喂狗,防止IWDG复位
// 当计数器的值减到0的时候会产生系统复位
IWDG_ReloadCounter();
}
iwdg.h
#ifndef _IWDG_H
#define _IWDG_H
#include "stm32f10x.h"
void IWDG_Feed(void);
void IWDG_Config(uint8_t prv ,uint16_t rlv);
#endif
main.c
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "iwdg.h"
#include "systick.h"
int main()
{
led_gpio_init();
SysTick_Delay_ms(1000);
if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
{
/* 独立看门狗复位 */
/* 亮红灯 */
LED0(1);;
/* 清除标志 */
RCC_ClearFlag();
/*如果一直不喂狗,会一直复位,加上前面的延时,会看到红灯闪烁
在1s 时间内喂狗的话,则会持续亮绿灯*/
}
else
{
/*不是独立看门狗复位(可能为上电复位或者手动按键复位之类的) */
/* 亮蓝灯 */
LED1(1);
}
// 配置按键GPIO
Key_GPIO_Config();
// IWDG 1s 超时溢出
IWDG_Config(IWDG_Prescaler_64 ,625);
//while部分是我们在项目中具体需要写的代码,这部分的程序可以用独立看门狗来监控
//如果我们知道这部分代码的执行时间,比如是500ms,那么我们可以设置独立看门狗的
//溢出时间是600ms,比500ms多一点,如果要被监控的程序没有跑飞正常执行的话,那么
//执行完毕之后就会执行喂狗的程序,如果程序跑飞了那程序就会超时,到达不了喂狗
//的程序,此时就会产生系统复位。但是也不排除程序跑飞了又跑回来了,刚好喂狗了,
//歪打正着。所以要想更精确的监控程序,可以使用窗口看门狗,窗口看门狗规定必须在
//规定的窗口时间内喂狗。
while(1)
{
// 这里添加需要被监控的代码,如果有就去掉按键模拟喂狗,把按键扫描程序去掉
//--------------------------------------------------------------------------
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
{
// 喂狗,如果不喂狗,系统则会复位,复位后亮红灯,如果在1s
// 时间内准时喂狗的话,则会亮绿灯
IWDG_Feed();
//喂狗后亮绿灯
LED2(1);;
}
}
}
?
?
?
?