前言
使用的是正点原子的探索者开发板进行学习,芯片:STM32F407ZGTx
学习说明此文档为本人的学习笔记,注重实践,关于理论部分会给出相应的学习链接。
注:本文档添加了对代码的在线调试功能,有助于大家更好理解相关寄存器和重要变量值的变化
理论学习
一、看门狗(WDG)简介
在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog) 。
在启动正常运行的时候,系统不能复位,可以通过看门狗来复位单片机。
在系统跑飞(程序异常执行)的情况,系统复位,程序重新执行。
具有两个嵌入式看门狗外设,具有安全性高、定时准确及使用灵活的优点。两个看门狗外设(独立和窗口)均可用于检测并解决由软件错误导致的故障;当计数器达到给定的超时值时,触发一个中断(仅适用于窗口型看门狗)或产生系统复位。
二、独立看门狗(IWDG)
2.1 IWDG简介
2.2 IWDG特征
- 自由运行递减计数器
- 时钟由独立RC振荡器提供(可在待机和停止模式下运行)
- 当递减计数器值达到0x000时产生部位(看门狗激活时)
2.3 IWDG功能
在键值寄存器(IWDG_KR)中写入0xCCCC,开始启用独立看门狗。此时计数器开始从其复位值0xFFF递减,当计数器值计数到尾值0x000时会产生一个复位信号(IWDG_RESET)。
无论何时,只要在键值寄存器IWDG_KR中写入0xAAAA(通常说的喂狗), 自动重装载寄存器IWDG_RLR的值就会重新加载到计数器,从而避免看门狗复位。
如果程序异常,就无法正常喂狗,从而系统复位。
- 硬件看门狗
- 寄存器访问保护
- 调试模式(请参考芯片手册)
![](https://i-blog.csdnimg.cn/blog_migrate/6bccaa32d82dd49b98d9ff8a0a9dd0e0.png)
2.4 IWDG相关寄存器
- 键值寄存器(IWDG_KR): 0~15位有效(只写,读为0000h)
必须每隔一段时间通过软件对这些位写入键值AAAAh,否则当计数器计数到0时,看门狗产生复位。写入键值 5555h 可使能对 IWDG_PR 和 IWDG_RLR 寄存器的访问;写入键值 CCCCh 可启动看门狗(选中硬件看门狗选项的情况除外)
- 预分频寄存器(IWDG_PR):对时钟(LSI)进行分频
- 重载寄存器(IWDG_RLR):看门狗计数器重载值
受写保护,每次对 IWDR_KR 寄存器写入值 AAAAh 时,这个值就会重装载到看门狗计数器中。之后,看门狗计数器便从该装载的值开始递减计数。超时周期由该值和时钟预分频器共同决定。
- 状态寄存器(IWDG_SR):只有RVU、PVU两位有效
RVU:看门狗计数器重载值更新。可通过硬件将该位置 1 以指示重载值正在更新。当在 VDD 电压域下完成重载值更新操作后 (需要多达 5 个 RC 40 kHz 周期),会通过硬件将该位复位。
重载值只有在 RVU 位为 0 时才可更新。
PVU:看门狗预分频器值更新。可通过硬件将该位置 1 以指示预分频器值正在更新。当在 VDD 电压域下完成预分频器值更新操作后(需要多达 5 个 RC 40 kHz 周期),会通过硬件将该位复位。
三、窗口看门狗(WWDG)
3.1 WWDG简介
3.2 WWDG特征
- 可编程的自由运行递减计数器
- 复位条件
- 当递减计数器值小于 0x40 时复位(如果看门狗已激活)
- 在窗口之外重载递减计数器时复位(如果看门狗已激活)
-
提前唤醒中断 (EWI) :当递减计数器等于 0x40 时触发(如果已使能且看门狗已激活)
3.3 WWDG功能
STM32F的窗口看门狗中有一个7位的递减计数器T[6:0],它会在出现下述2种情况之一时产生看门狗复位:
- 当喂狗的时候如果计数器的值大于某一设定数值W[6:0]时,此设定数值在WWDG_CFR寄存器定义。
- 当计数器的数值从0x40减到0x3F时【T6位跳变到0】
如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可以用于喂狗以避免WWDG复位。
实战演练
一、独立看门狗
1.1 设计规划
- 实验目标
在配置在配置看门狗后,DS0 将常亮,如果 KEY_UP 按键按下,就喂狗(重新状态计数器),只要 KEY_UP 不停的 按,看门狗就一直不会产生复位,保持 DS0 的常亮,一旦超过看门狗定溢出时间(Tout)还没 按,那么将会导致程序重启,这将导致 DS0 熄灭一次。
- 硬件资源
1.2 程序设计
- 配置步骤
- 向IWDG_KR写入0X5555(取消 IWDG_PR 和 IWDG_RLR 的写保护,且可以设置) 预分频和重装载,看门狗喂狗时间(看门狗溢出时间),计算公式: Tout = ((4x2^peer) x rlr) / 32 Tout为看门狗溢出时间(ms); prer为看门狗时钟预分频值(IWDG_PR值,范围0~7);rlr为看门狗的重装载值(IWDG_RLR的值) 如:Tout = ((4x2^4) x 500/32 = 64 x 500 /32 = 1000ms (由于时钟不准,所以喂狗时间不能太晚)
-
向 IWDG_KR 写入 0XAAAA(喂狗)
-
向 IWDG_KR 写入 0XCCCC(启动看门狗) 一旦启动就不能再关闭,只有重启时才会关闭,重启后不会自动打开IWDG,慎用。
- IWDG配置
iwdg.h
#ifndef _IWDG_H
#define _IWDG_H
#include "sys.h"
void IWDG_Init(u8 per,u16 rlr);
void IWDG_Feed(void);
#endif
iwdg.c
#include "iwdg.h"
//IWDG->PR[2:0]=000:111 2^prer prer=(0~7)
//IWDG->RLR[11:0]=0X000:0XFFF rlr=(0~4095)
//OUT TIME£ºTout=((4*2^prer)*rlr)/32 ms
void IWDG_Init(u8 prer,u16 rlr)
{
IWDG->KR = 0X5555; //openen : IWDG->PR;IWDG->RLR
IWDG->PR = prer;
IWDG->RLR = rlr;
IWDG->KR = 0XAAAA; //reload
IWDG->KR = 0XCCCC; //IWDGen
}
void IWDG_Feed(void)
{
IWDG->KR = 0XAAAA;
}
- 主函数text.c
#include "led.h"
#include "key.h"
#include "iwdg.h"
int main(void)
{
Stm32_Clock_Init(336,8,2,7);
delay_init(168);
LED_Init(); //??
KEY_Init(); //??
delay_ms(100); //??
IWDG_Init(4,500); //??
LED0=0; //??
while(1)
{
if(KEY_Scan(0)==WKUP_PRES)//??WK_UP??,???
{
IWDG_Feed();//??
}
delay_ms(10);
};
}
1.3 在线调试
查看IWDG定时器,可以看到KE无论向里面写什么数,读出都是0。而PR和RLR里面也有相关值
1.4 上板验证
编译后无错误下载到开发板中,会发现DS0没隔1s进行闪烁(不复位常亮,复位灭),如果按击WK_UP按键,则不会复位,DS0就会一直亮。
二、窗口看门狗
1.1 设计规划
- 实验目标
- 硬件资源
1.2 程序设计
- 配置步骤
- 使能WWDG时钟 :挂在PCK1时钟,需要先使能时钟。
- 设置WWDG_CFR、WWDG_CR两个寄存器:包括使能窗口看门狗、开启中断、设置计数器的初始值、设置窗口值并设置分频数 WDGTB 等。
- 开启WWDG中断并分组
- 编写中断服务函数:喂狗
- WWDG配置
wwdg.h
#ifndef _WWDG_H
#define _WWDG_H
#include "sys.h"
void WWDG_Init(u8 tr, u8 wr , u8 fprer);
void WWDG_Set_Counter(u8 cnt) ; //喂狗
#endif
wwdg.c
#include "wwdg.h"
#include "sys.h"
#include "led.h"
u8 WWDG_CNT = 0X7F; //±£´æWWDG¼ÆÊýÆ÷µÄÉèÖÃÖµ£¬Ä¬ÈÏΪ×î´ó
//tr :T[6:0] ,counter
//wr :w[6:0] ,´°¿ÚÖµ
//fprer£º·ÖƵϵÊý
//Fwwdg=PCLK1/(4096*2^fprer) //Ò»°ãPCLK1 = 42MHz
void WWDG_Init(u8 tr, u8 wr , u8 fprer)
{
RCC->APB1ENR|=1<<11; //?? wwdg ??
WWDG_CNT = tr & WWDG_CNT;//??? WWDG_CNT.
WWDG->CFR|=fprer<<7; //PCLK1/4096 ?? 2^fprer
WWDG->CFR&=0XFF80;
WWDG->CFR|=wr; //?????
WWDG->CR|=WWDG_CNT;//??????
WWDG->CR|=1<<7; //?????
// MY_NVIC_Init(2,3,WWDG_IRQn,2);
SCB->AIRCR |= 0X05FA0000 | 0X500;
NVIC->IP[0] |= 0XF0;
NVIC->ISER[0] |= 1<<0;
WWDG->SR=0X00; //»½ÐÑÖжϱêÖ¾
WWDG->CFR|=1<<9; //ʹÄÜ»½ÐÑÖжÏ
}
void WWDG_Set_Counter(u8 cnt)
{
WWDG->CR =(cnt & 0x7F);
void WWDG_IRQHandler(void)
{
WWDG_Set_Counter(WWDG_CNT);
WWDG->SR=0X00;
LED1=!LED1;
}
- 主函数text.c
#include "key.h"
#include "wwdg.h"
int main(void)
{
Stm32_Clock_Init(336,8,2,7);
delay_init(168);
LED_Init();
// KEY_Init();
// IWDG_Init(4,500);
LED0=0;
delay_ms(300);
WWDG_Init(0X7F,0X5F,3);
while(1)
{
// if(KEY_Scan(0)==WKUP_PRES)//??WK_UP??,???
// {
// IWDG_Feed();//??
// }
// delay_ms(10);
LED0=1;
};
}