更多交流欢迎关注作者抖音号:81849645041
STM32 有两个看门狗,一个是独立看门狗,一个是窗口看门狗。我们知道独立看门狗的工作原理就是一个递减计数器在 LSI 时钟的驱动下不断的往下递减计数,当减到 0 之前如果 没有刷新递减计数器的值(即俗称的喂狗)的话,产生复位。窗口看门狗跟独立看门狗一 样,也是一个递减计数器不断的往下递减计数,当减到一个固定值 0x40 时还不喂狗的话, 产生复位,这个值叫窗口的下限,是固定的值,不能改变。这个是跟独立看门狗类似的地方,不同的地方是窗口看门狗的计数器的值在减到某一个数之前喂狗的话也会产生复位,这 个值叫窗口的上限,上限值由用户独立设置。窗口看门狗计数器的值必须在上窗口和下窗口 之间才可以喂狗,这就是窗口看门狗中窗口两个字的含义。独立看门狗和窗口看门狗的区别如下:
RLR 是重装载寄存器,用来设置独立看门狗的计数器的值。 TR 是窗口看门狗的计数器 的值,由用户独立设置,WR 是窗口看门狗的上窗口值,由用户独立设置。 窗口看门狗功能框图如下: 1 : 窗口看门狗时钟:窗口看门狗时钟来自 PCLK1 , PCLK1 最大是 42M ,由 RCC 时钟 控制器开启。 2 : 计数器时钟:计数器时钟由 CK 计时器时钟经过预分频器分频得到,分频系数由配 置寄存器 CFR 的位 8:7 WDGTB[1:0] 配置,可以是 [0,1,2,3] ,其中 CK 计时器时钟 =PCLK1/4096 , 除 以 4096 是 手 册 规 定 的 。 所 以 计 数 器 的 时 钟 CNT_CK=PCLK1/4096/(2^WDGTB) ,这就 可以算出 计数器减 一个数的时 间 T= 1/CNT_CK = Tpclk1 * 4096 * (2^WDGTB) 。 3 : 计数器:窗口看门狗的计数器是一个递减计数器,共有 7 位,其值存在控制寄存器 CR 的位 6:0 ,即 T[6:0] ,当 7 个位全部为 1 时是 0x7F ,这个是最大值,当递减到 T6位变成 0 时,即从 0x40 变为 0x3F 时候,会产生看门狗复位。这个值 0x40 是看门狗能够递减到的最小值,所以计数器的值只能是:0x40~0x7F 之间,实际上用来计数的是 T[5:0] 。当递减计数器递减到 0x40 的时候,还不会马上产生复位,如果使能了提前唤醒中断:CFR 位 9 EWI 置 1 ,则产生提前唤醒中断,如果真进入了这个中断的话,就说明程序肯定是出问题了,那么在中断服务程序里面我们就需要做最重要的工作,比如保存重要数据,或者报警等,这个中断我们也叫它死前中断。 4 : 窗口值:我们知道窗口看门狗必须在计数器的值在一个范围内才可以喂狗,其中下 窗口的值是固定的 0x40 ,上窗口的值可以改变,具体的由配置寄存器 CFR 的位 6:0 W[6:0] 设置。其值必须大于 0x40 ,如果小于或者等于 0x40 就是失去了窗口的价值, 而且也不能大于计数器的值,所以必须得小于 0x7F 。那窗口值具体要设置成多大? 这个得根据我们需要监控的程序的运行时间来决定。如果我们要监控的程序段 A 运 行的时间为 Ta ,当执行完这段程序之后就要进行喂狗,如果在窗口时间内没有喂狗 的话,那程序就肯定是出问题了。一般计数器的值 TR 设置成最大 0x7F ,窗口值为 WR ,计数器减一个数的时间为 T ,那么时间: (TR-WR)*T 应该稍微大于 Ta 即可, 这样就能做到刚执行完程序段 A 之后喂狗,起到监控的作用,这样也就可以算出 WR 的值是多少。 5 : 计算看门狗超时时间 从图我们知道看门狗超时时间: Twwdg = Tpclk1 x 4096 x2^wdgtb x (T[5:0] + 1) ms ,当 PCLK1 = 30MHZ 时, WDGTB 取不同的值时有最小和最大的超时时间。下面讲解一下,最大 WDGTB=0 是怎么算的。递减计数器有 7 位 T[6:0] ,当位 6 变为 0 的时候就会产生复位,实 际上有效的计数位是 T[5:0] ,而且 T6 必须先设置为 1 。如果 T[5:0]=0 时,递减计数器再减一 次,就产生复位了,那这减一的时间就等于计数器的周期 =1/CNT_CK = Tpclk1 * 4096 * (2^WDGTB) = 1/30 * 4096 *2^0 = 136.53us ,这个就是最短的超时时间。如果 T[5:0] 全部装满 为 1 , 即 63 , 当 他 减 到 0x40 变 成 0x3F 时 , 所 需 的 时 间 就 是 最 大 的 超 时 时 间 =113.7*2^5=136.53*64=8.74ms 。同理,当 WDGTB 等于 1/2/3 时,代入公式即可。 WWDG 一般被用来监测,由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的 运行序列而产生的软件故障。比如一个程序段正常运行的时间是 50ms ,在运行完这个段程序 之后紧接着进行喂狗,如果在规定的时间窗口内还没有喂狗,那就说明我们监控的程序出故 障了,跑飞了,那么就会产生系统复位,让程序重新运行。 实验准备 MDK5 开发环境。 STM32F4xx HAL 库。 STM32F407 开发板。 STM32F4xx 参考手册。 STM32F407 开发板电路原理图。 实验步骤 1 、 首先创建 WWDG_Init() 函数,初始化窗口看门狗并使能中断及优先级。配置计数器值 为 0x7F ( 127 ,最大值),配置窗口值为 0x50 ( 80 , 0x40 为最小值),计数器分频为 8 分 频,则计数器频率为 (PCLK (42MHz) / 4096) / 8 约等于 1281Hz ,则计数器记一次时间为 780us ,则有 780 * (127 - 80) = 36.6ms < 刷新窗口 < ~780 * 64 ( 0x40 ) = 49.9ms 。 WWDG_HandleTypeDef WWDG_Handle; // 窗口看门狗初始化句柄 void WWDG_Init(void) { __HAL_RCC_WWDG_CLK_ENABLE(); // 窗口看门狗时钟使能 WWDG_Handle.Instance = WWDG; // 窗口看门狗基地址 WWDG_Handle.Init.Counter = 0x7F; // 计时器值初始化为最大 127 WWDG_Handle.Init.Prescaler = WWDG_PRESCALER_8; // 预分频值 8 分频 WWDG_Handle.Init.Window = 0x50; // 窗口值 80 WWDG_Handle.Init.EWIMode = WWDG_EWI_ENABLE; // 看门狗使能 HAL_WWDG_Init(&WWDG_Handle); // 窗口看门狗初始化 HAL_NVIC_SetPriority(WWDG_IRQn, 2, 3); // 配置窗口看门狗中断优先级 HAL_NVIC_EnableIRQ(WWDG_IRQn); // 使能窗口看门狗 } 2 、 配置窗口看门狗中断服务函数。当递减计数器减到 0x40 的时候,会产生一个中断,这 个中断我们称它为死前中断或者遗嘱中断。在本实验的中断服务函数中我们开启蜂鸣器。 // 窗口看门狗遗嘱中断 , 当计数器的值减到 0x40 后,产生的中断 void WWDG_IRQHandler(void) { HAL_WWDG_IRQHandler(&WWDG_Handle); } // 中断回调函数 void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg) { BUZZER_ON; } 3 、 定义喂狗函数。喂狗就是重新刷新递减计数器的值防止系统复位,喂狗一般是在主函 数中喂。 // 喂狗函数 void WWDG_Refresh(void) { HAL_WWDG_Refresh(&WWDG_Handle); } 4 、 主函数 main 程序如下: 第一步:定义存储计数器当前值和窗口上限值变量。 第二步:初始化系统时钟、蜂鸣器,开启蜂鸣器并延时 1 秒钟。 第三步:初始化窗口看门狗并读取窗口上限值。 第四步:在 while() 循环中首先关闭蜂鸣器,然后不断读取计数器的值,当计数器的值减 小到小于上窗口值的时候,我们喂狗,让计数器重新计数。 uint16_t wwdg_tr; // 读取计数器当前值 uint16_t wwdg_wr; // 读取窗口上限值 int main() { CLOCK_Init(); // 时钟初始化 BUZZER_Init(); // 蜂鸣器初始化 WWDG_Init(); // 窗口看门狗初始化 BUZZER_ON; // 开启蜂鸣器 // 读取窗口上限值 wwdg_wr= WWDG->CFR & 0x7F; while(1) { BUZZER_OFF; // 关闭蜂鸣器 // 读取计数器当前值 wwdg_tr = WWDG->CR & 0x7F; // 当计数器的值小于窗口上限值时,触发喂狗事件 if(wwdg_tr < wwdg_wr) { WWDG_Refresh(); } } } 5 、 编译整个工程并且下载程序到开发板。 实验现象 将程序下载到开发板中,蜂鸣器响起,一段时间后关闭,之后蜂鸣器将不再响起,说明 系统没有产生复位。