关于stm32F103的窗口看门狗最详细的讲解,真正的手把手教新手入门级的教程(库函数版本)
简单介绍了窗口看门狗,详细的工作方式和寄存器的配置。浅浅的解析了一下代码
第一次写教程,如有错误请反馈,有什么问题我也会及时回答的(⁄ ⁄•⁄ω⁄•⁄ ⁄)
前言
在初学stm32的路上,会遇到看门狗这个概念,在我学习的时候,独立看门狗很好理解,但是窗口看门狗有点难以理解,以及配置相关寄存器和使用库函数时都需要思考很久,所以写下这篇一遍加强记忆,希望对大家有用。
一、看门狗是什么
当程序运行时,难免会由外界的干扰导致程序可能跑飞进入死循环,而看门狗就是为了防止发生这样的情况。
1.窗口看门狗是什么
其实本该先讲一下独立看门狗,但是主要以窗口看门狗为主,有时间以后会发独立看门狗的详细见解。
窗口看门狗由APB1时钟分频后得到36MHZ(PCLK1)的时钟频率(前提是系统时钟频率为72MHZ)
可以看到由PCLK1再到WDGTB(一个看门狗预分频器)作为时钟。
简单了解了频率,接下来讲一下概念。
对于一般的看门狗,程序可以在它产生复位前的任意时刻刷新看门狗,但这有一个隐患,有可能程序跑乱了又跑回到正常的地方,或跑乱的程序正好执行了刷新看门狗操作,这样的情况下一般的看门狗就检测不出来了,如果使用窗口看门狗,程序员可以根据程序正常执行的时间设置刷新看门狗的一个时间窗口,保证不会提前刷新看门狗也不会滞后刷新看门狗,这样可以检测出程序没有按照正常的路径运行非正常地跳过了某些程序段的情况。
记住上面的一段话“保证不会提前刷新看门狗也不会滞后刷新看门狗”之后的寄存器配置我会详细讲解。
2.独立看门狗是什么
独立看门狗的时钟由独立的 RC振荡器 LSI提供,即使主时钟发生故障它仍然有效,非常独立。LSI的频率一般在 30~60KHZ之间,根据温度和工作场合会有一定的漂移,我们一般40KHZ,所以独立看门狗的定时时间不一定非常精确,只适用于对时间精度要求比较低的场合。
注意是对时间精度要求比较低的场合。
二、使用步骤
1.详细了解它的工作方式
如图上图可以看到,T[6:0]是WWDG_CR寄存器的低7位,对T[6:0]可以配置一个递减计数器的初值,第8位是使能看门狗;至于上面说“当计数值从40h变为3Fh时,从二进制看就是T6变为0,从而产生看门狗复位”看下图12.1.1
当CR寄存器的T[6:0]的值降至0x3F时,产生了复位信号且T6位置0,这里T6置0和后面的电路图有关,先记住T6位置0。
再看上图,看一下W[6:0]相关介绍和图12.1.1对照看一下,这里的意思是如果在T[6:0]内的值大于W[6:0]时再去进行一个喂狗这样的操作,那么就会产生一个复位信号,结合之前的T[6:0]的值降至0x3F时也产生了复位信号,是不是可以解释“保证不会提前刷新看门狗也不会滞后刷新看门狗”,不能理解的话再去看看图12.1.1, 在时间轴上有一段不允许刷新的时间, 不能理解也没关系,还要从电路图去解析如下图
这里再附上国际逻辑门符号
开始推导
先说第一种情况:当T[6:0]内的值大于W[6:0]时喂狗,是如何产生复位信号。
当T[6:0]内的值大于W[6:0]时,一个与门上是1,此时做出了喂狗的动作,也是1,1&1=1;T6此时肯定为1,这个T6不能理解的请用二进制去看,T6的非 或上 刚才的1 等于1;WDGA是使能看门狗的,此时肯定为1(为0递减计数器都不会运作),1&WDGA=1;产生了复位信号。
再说一下第二种情况:当T[6:0]内的值小于W[6:0]时喂狗,比较结果为0;0&1=0;T6此时还是为1,0 或上 T6的非 等于0;0&WDGA=0;不会产生复位信号。
再来第三种情况:当T[6:0]内的值等于0x3F是,注意T6为0了现在,仍然是T[6:0]内的值小于W[6:0]是喂狗,结果为0;0&1=0;0 或上 T6的非 等于1;1&WDGA=1;产生了复位信号。
这样是不是很清晰了呢,看不懂上述的过程去了解一下逻辑门。至此已经了解了它的工作方式,可以去了解寄存器的配置了。
2.简单了解一下窗口看门狗的寄存器配置
先放出三张寄存器的图片
有两个已经讲过,但是我讲的不是很详细,一定要仔细看看。
再放出一张配置超时时间公式表
如上图这里的T[5:0]不妨去思考一下为什么,想一会再往下看
解释一下:窗口看门狗的超时时间可不是T[6:0],因为在递减计数器等于0x40时就产生一个看门狗中断,所以拿T[6:0]减去0x40=T[5:0],因为T[6:0]=2^7-1=127,0x40=64,T[5:0]=2^6-1=63;这个看门狗中断要去喂狗,这样就不会产生复位信号了,这就是T[5:0]的由来。
知道了公式就让我们来验证一下吧,如下图
这里自己用计算器算算吧。
3.配置寄存器去工作的流程
快要接近尾声了,再坚持一下,接下来讲写代码思路和过程。这里是库函数的配置,其他库几乎同理。
1.RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // WWDG 时钟使能。
2.void WWDG_SetWindowValue(uint8_t WindowValue);//这个函数的入口参数 WindowValue 用来设置看门狗的上窗口值。也就是W[6:0]的值。
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);//这个函数同样只有一个入口参数,用来设置看门狗的分频值。
3.WWDG_EnableIT(); //开启窗口看门狗中断。
4.void WWDG_Enable(uint8_t Counter);//使能窗口看门狗,Counter是设置递减计数器的初值,我会在下面的代码给你们解释。
5.编写中断服务函数(太多了放在后面的代码里面讲解)。
三.写代码
这里只讲main函数和WWDG函数
首先是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 初始化
}
void WWDG_IRQHandler(void)
{
WWDG_SetCounter(WWDG_CNT); //当禁掉此句后,窗口看门狗将产生复位
WWDG_ClearFlag(); //清除提前唤醒中断标志位
LED1=!LED1; //LED 状态翻转
}
在函数WWDG_Init(u8 tr,u8 wr,u32 fprer)里面有“配置寄存器去工作的流程”的流程。
1.首先去使能APB1,因为要去使用PCLK1 。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
2.设置 IWDG 预分频值
WWDG_SetPrescaler(fprer);
3.设置窗口值
WWDG_SetWindowValue(wr);
4.使能看门狗,设置 counter
WWDG_Enable(WWDG_CNT);
详细看一下此函数的具体定义:
/**
* @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)
{
/* Check the parameters */
assert_param(IS_WWDG_COUNTER(Counter));
WWDG->CR = CR_WDGA_Set | Counter;
}
在这里的Counter是递减计数器的值的设置最大只有0x7F,应为只有7位;CR_WDGA_Set看一下具体的宏定义#define CR_WDGA_Set ((uint32_t)0x00000080),也就是CR_WDGA_Set | Counter是对CR寄存器进行了使能窗口看门狗和对递减计数器赋值。
5.清除中断标志位(CFR寄存器会在0x40触发中断),不清除中断位,下一次的中断将会出错(具体什么会有什么问题我没有试过,你们可以试一试。)
WWDG_ClearFlag();
6.设置中断号,分配优先级,并使能该中断,最后初始化,是这个函数的具体内容。
WWDG_NVIC_Init(); //初始化窗口看门狗 NVIC
7.开启窗口看门狗中断
WWDG_EnableIT(); //开启窗口看门狗中断
8.在触发窗口看门狗中断时,进入该函数WWDG_NVIC_Init(),该函数的具体内容如下
void WWDG_IRQHandler(void)
{
WWDG_SetCounter(WWDG_CNT); //当禁掉此句后,窗口看门狗将产生复位
WWDG_ClearFlag(); //清除提前唤醒中断标志位
LED1=!LED1; //LED状态翻转
}
在WWDG_SetCounter(WWDG_CNT)函数内部是这样的
/**
* @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)
{
/* Check the parameters */
assert_param(IS_WWDG_COUNTER(Counter));
/* Write to T[6:0] bits to configure the counter value, no need to do
a read-modify-write; writing a 0 to WDGA bit does nothing */
WWDG->CR = Counter & BIT_Mask;
}
Counter就是重载递减计数器的初值,#define BIT_Mask ((uint8_t)0x7F)可以看到除了第8位为0,低7位为1,不需要再次对看门狗使能。
再说main函数
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init();
KEY_Init(); //按键初始化
LED0=0;
delay_ms(300);
WWDG_Init(0X7F,0X5F,WWDG_Prescaler_8);//计数器值为7f,窗口寄存器为5f,分频数为8
while(1)
{
LED0=1;
}
}
这个自己去理解吧
最后实现的功能是通过 LED0(DS0)来指示是否正在初始化。而 LED1(DS1)用来指示是否发生了中 断。我们先让 LED0 亮 300ms,然后关闭以用于判断是否有复位发生了。在初始化 WWDG 之后,我们回到死循环,关闭 LED1,并等待看门狗中断的触发/复位。
结束!!!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)