STM32F407ZG开发板学习(5)
看门狗简介
每隔一段时间需要检测到MCU发来的信号(喂狗),否则会给MCU一个复位信号,使其复位,以防止程序发生死循环。
独立看门狗 (IWDG) 由其专用低速时钟 (LSI) 驱动,因此即便在主时钟发生故障时仍然保持工作状态。窗口看门狗 (WWDG) 时钟由 APB1 时钟经预分频后提供,通过可配置的时间窗口来检测应用程序非正常的过迟或过早的操作。
独立看门狗IWDG
IWDG 最适合应用于那些需要看门狗作为一个在主程序之外,能够完全独立工作,并且对时 间精度要求较低的场合。
当通过对关键字寄存器 (IWDG_KR) 写入值 0xCCCC 启动独立看门狗时,计数器开始从复位值 0xFFF 递减计数。当计数器计数到终值 (0x000) 时会产生一个复位信号(IWDG 复位)。
任何时候将关键字 0xAAAA 写到 IWWDG_KR 寄存器中,IWDG_RLR 的值就会被重载到计数器,从而避免产生看门狗复位,俗称喂狗操作。
原理
IWDG的原理比较简单,预先设置好重载值,开启看门狗后,重载值就会递减,当递减到0会产生一个IWDG复位信号,把程序复位一次。
IWDG_KR 关键字寄存器
官方文档已经讲得很清楚了,写入0xAAAA就是所谓的喂狗操作。
(这里实际我有个疑问,如果KR实际的状态就只有0000h、5555h、AAAAh、CCCCh的话,为什么不用两个bit或4个bit标记状态来取代呢?还是说需要16位来做一些什么)
IWDG_PR 预分频寄存器
实际上就3个bit,表示8种分频数。
IWDG_RLR 重载值寄存器
12位,因此最大计数为0xFFF。
IWDG_SR 状态寄存器
两位,两个分别表示更新RLR和PR的失能/使能。
窗口门狗WWDG
窗口看门狗(WWDG )通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。
窗口看门狗跟独立看门狗一样,也是一个递减计数器不断的往下递减计数,当减到一个固定值 0X40 时还不喂狗的话,产生复位,这个值叫窗口的下限,是固定的值,不能改变。这个是跟独立看门狗类似的地方,不同的地方是窗口看门狗的计数器的值在减到某一个数之前喂狗的话也会产生复位,这个值叫窗口的上限,上限值由用户独立设置。窗口看门狗计数器的值必须在上窗口和下窗口之间才可以喂狗,这就是窗口看门狗中窗口两个字的含义。
原理
顾名思义,窗口看门狗只允许在某一个时间段执行喂狗操作,如下方的图,与独立看门狗一样,它也是通过一个计数值CNT的递减,但它同时有一个窗口值CFR来规定允许喂狗的时间段。这意味着从计数递减到窗口值的这段时间,喂狗操作无效,当计数递减至0,同样会产生复位。但WWDG还为我们提供一个复位前的中断,我们可以在中断处理程序的出错原因并在其中考虑是否喂狗,详情见
计数器
的介绍。
计数器
窗口看门狗的计数器是一个递减计数器,共有 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,则产生提前唤醒中断
,如果真进入了这个中断的话,就说明程序肯定是出问题了,那么在中断服务程序里面我们就需要做最重要的工作,比如保存重要数据,或者报警等,这个中断我们也叫它死前中断。
窗口值
我们知道窗口看门狗必须在计数器的值在一个范围内才可以喂狗,其中下窗口的值是固定的 0X40,上窗口的值可以改变,具体的由配置寄存器 CFR的位 6:0 W[6:0]设置。其值
必须大于 0X40
,如果小于或者等于 0X40就是失去了窗口的价值,而且也不能大于计数器的值,所以
必须得小于 0X7F
。
那窗口值具体要设置成多大?这个得根据我们需要监控的程序的运行时间来决定。如果我们要监控的程序段 A运行的时间为 Ta,当执行完这段程序之后就要进行喂狗,如果在窗口时间内没有喂狗的话,那程序就肯定是出问题了。
一般计数器的值 TR设置成最大 0X7F,窗口值为 WR,计数器减一个数的时间为 T,那么时间:(TR-WR)*T应该稍微大于 Ta即可,这样就能做到刚执行完程序段 A之后喂狗,起到监控的作用,这样也就可以算出 WR的值是多少。
计数器时钟
计数器时钟由 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)。
超时值
看门狗超时时间: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时,所需的时间就是最大的超时时间=136.53 * 64=8.74ms。同理,当 WDGTB等于 1/2/3 时,代入公式即可。
WWDG_CR 控制寄存器
使能位及计数值。
WWDG_CFR 配置寄存器
死前中断的使能位、分频数及窗口值。
WWDG_SR 状态寄存器
就1位,死前中断的标志。
实验
IWDG
配置IWDG,设置一个LED灯为常亮,当进入while(1)主循环后,如果不喂狗,会产生复位,即灯会一闪一闪的。在循环里设置按键喂狗,则连续按键灯常亮,不按键灯一闪一闪。
配置
超时周期的计算:T=((4×2^PR)×RLR)/40 ms,表里写的也比较清楚,实际最小和最大就是将RLR赋值最小0x001和最大0xFFF。
配置步骤:
- 取消寄存器写保护,即向KR写入0x5555。
- 设置预分频(PR)和重装载值(RLR),则看门狗溢出时间为T=((4×2^PR)×RLR)/40,单位ms。
- 重载计数值喂狗(KR写入0xAAAA)
- 启动看门狗(KR写入0xCCCC)
代码如下:
prer和rlr是用户设置的分频数和重装值
IWDG_WriteAccessCmd(ENABLE); //关写保护,即KR写入0x5555
IWDG_SetPrescaler(prer); //预分频
IWDG_SetReload(rlr); //重装载值
IWDG_ReloadCounter(); //喂狗,即KR写入0xAAAA
IWDG_Enable(); //使能IWDG
喂狗
即reload重装值。
IWDG_ReloadCounter(); //喂狗
WWDG
配置WWDG,初始化时点亮LED0并延时一段时间,主死循环中熄灭LED0用于判断程序是否会复位,中断函数中喂狗并转换LED1的引脚值。
期望实验结果:LED0初始化过程中亮,过一会熄灭,待计数从第一次进入中断函数开始,LED1会不停闪烁(一秒20次左右,时钟约42MHz,1/42×4096×8×64≈49.9ms一次)。这说明在中断函数中喂狗后程序不会复位,因为是死前中断。
配置
- 时钟使能,由于时钟使用的是APB1的分频。
- 中断初始化,用NVIC_InitTypeDef结构体。
- 设置计数值(6位TR)、窗口值(要大于0x40)、分频数(三位即0~7)
- 使能开门狗
-
清除提前唤醒中断标志位(SR寄存器重置,EWIF设为0),再开启提前唤醒中断(WWDG_EnableIT,即EWI置1)
EWIF:当计数器值达到 0x40 时此位由硬件置 1。它必须由软件通过写入 0 来清零。写入 1 不起作用。如果不使能中断,此位也会被置 1。
EWI:置 1 后,只要计数器值达到 0x40 就会产生中断。此中断只有在复位后才由硬件清零。 - 实例void WWDG_IRQHandler(void); 中断服务程序。
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;
NVIC_Init(&NVIC_InitStructure);
WWDG_CounterValue &= tr; //WWDG_CounterValue是在全局设置的一个初始值为0x7F的uint8数
WWDG_SetPrescaler(fprer); //分频因子
WWDG_SetWindowValue(WWDG_WindowValue); //设置窗口值
WWDG_SetCounter(WWDG_CounterValue); //设置计数值
WWDG_Enable(WWDG_CounterValue); //使能看门狗
WWDG_ClearFlag(); //清除提前唤醒中断标志位
//此位仅在计数达0x40时被硬件置1,表示要中断了,需要在中断函数中喂狗时由软件清0
WWDG_EnableIT(); //开启提前唤醒中断
中断函数:
void WWDG_IRQHandler(void)
{
WWDG_SetCounter (WWDG_CounterValue); //重设窗口看门狗值,即喂狗操作,否则将复位
WWDG_ClearFlag(); //清除提前唤醒中断标志位
if(get_LED1())
{
LED1_ON();
}
else {
LED1_OFF();
}
}
main.c
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置系统中断优先级分组 2
LED_init();
delay_init(168);
LED0_ON();
delay_ms(300);
wwdg_init(0x7F, 0x5F, WWDG_Prescaler_8); //分频64,溢出时间1s
while(1)
{
LED0_OFF();
//进入死循环,由于在中断函数中设置了LED1的亮灭切换,则在不断进入中断喂狗时,LED1会不断切换。
//灯切换的时间间隔即两次产生中断的间隔,应该就是看门狗的等待时间
}
}