针对学习STM32窗口看门狗和原子战舰所给的WWDG例程时候,存在的问题做如下记录。
(1) 上窗口的值可以只有设定,7位二进制数最大只可以设定为127(0x7F),最小又必须大于下窗口的0x40,所以其取值范围为64~127(即:0x40~0x7F)
(2) 配置寄存器WWDG->CFR
窗口看门狗的时钟来自于PCLK1,在时钟配置中,其频率为外部时钟经倍频器后的二分频时钟,即为36MHz
(3)依据上述分析知,窗口时间计算公式为:
上窗口时间: T_min
= (WWDG_CR[6:0] - WWDG_CFR[6:0])/(Tck/(2^WDGTB))(us)
= (WWDG_CR[6:0] - WWDG_CFR[6:0])/((Tpck1/4096)/(2^WDGTB))(us)
= 4096 * (2^WDGTB)*(WWDG_CR[6:0] -WWDG_CFR[6:0])/(72/2) (us)
= 4096 *(2^WDGTB)*(WWDG_CR[6:0] - WWDG_CFR[6:0])/36 (us)
下窗口时间: T_max = 4096 * (2^WDGTB)*(WWDG_CR[6:0] - 0x3F)/36(us)
窗口看门狗的超时公式(刷新时间公式):
Twwdg = T_max - T_min
= 4096 *(2^WDGTB)*(WWDG_CFR[6:0] - 0x3F)/36 (us)
= 4096 *(2^WDGTB)*(WWDG_CFR[6:0] - 63)/36 (us)
又因为 WWDG_CFR[6:0]取值范围是:0~127,
(WWDG_CFR[6:0] –63)>0,
所以,
1<= (WWDG_CFR[6:0] – 63) <= 127-63=64
又因为 一个6位二进制X[5:0]数值范围是0~63,
1 <= X[5:0]+1 <= 64
所以,
上式可以表述为:
Twwdg = 4096 * (2^WDGTB)*(T[5:0]+1)/36 (us)
(4)依据上式,可以计算出WWDG的定时时间范围:
WDGTB(计数器分频值) |
最早喂狗时间/us |
最晚喂狗时间/ms |
0 |
113 |
7.28 |
1 |
227 |
14.56 |
2 |
455 |
29.12 |
3 |
910 |
58.25 |
正常情况下,烧录原子战舰所提供的WWDG库函数(v3.5)例程,会看到起初红灯点亮,后熄灭,黄灯开始闪烁。但是,烧录该程序,只看到红灯闪烁。这说明了WWDG产生了中断,但是中断函数中的黄灯闪烁语句(LED1=~LED1)并没有执行。
针对上述问题描述,可排除WWDG初始化错误的问题(因为红灯LED0在闪烁,说明WWDG已经产生的复位),定位到中断初始化失败或中断函数编写错误的问题。中断函数比较简单,检查后可以排除中断函数编写错误的问题。
下面,针对中断初始化函数进行检查。检查后,可以发现原例程中缺失了中断通道使能设置,即“NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //通道使能”。代码修改前后对比如下所示。修改后,再次烧录执行,会发现现象正常:起初红灯点亮,后熄灭,黄灯开始闪烁。
(1)中断初始化原始代码:
//WWDG中断初始化
void WWDG_NVIC_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //WWDG中断
//抢占2 子优先级3 组2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_Init(&NVIC_InitStructure);//NVIC 初始化
}
(2)中断初始化修改后代码:
//WWDG中断初始化
void WWDG_NVIC_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //WWDG中断
//抢占2 子优先级3 组2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //通道使能
NVIC_Init(&NVIC_InitStructure);//NVIC 初始化
}
(1)对于下述的WWDG初始化函数,烧录后,运行正常。但是,将WWDG使能并装载初始值语句WWDG_Enable(WWDG_CNT)放于清除提前唤醒中断标志位语句WWDG_ClearFlag()和开启窗口看门狗中断语句WWDG_EnableIT()之后,再次执行,现象异常:只有红色灯在闪烁,黄色灯一直熄灭。
正确程序:
//保存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_NVIC_Init();//初始化窗口看门狗 NVIC
WWDG_Enable(WWDG_CNT); //使能WWDG 并装载初始值
WWDG_ClearFlag();//清除提前唤醒中断标志位
WWDG_EnableIT(); //开启窗口看门狗中断
}
异常程序:
//保存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_NVIC_Init();//初始化窗口看门狗 NVIC
WWDG_ClearFlag();//清除提前唤醒中断标志位
WWDG_EnableIT(); //开启窗口看门狗中断
WWDG_Enable(WWDG_CNT); //使能WWDG 并装载初始值
}
(2)错因分析
执行WWDG_Enable(WWDG_CNT)语句,会使能WWDG,并将初始值CNT装载至计数器中。执行此句,还会将WWDG_SR的EWIF置1,造成程序无法进入中断处理函数。
用JLINK在keil中对此进行单步调试可以发现,执行WWDG_Enable(WWDG_CNT)语句之后,WWDG_SR的EWIF被置1,如下图所示。
第一步:执行WWDG_Enable(WWDG_CNT)语句之前,状态寄存器WWDG_SR=0x0000,即EWIF=0
第二步: 执行WWDG_Enable(WWDG_CNT)语句之后,状态寄存器WWDG_SR=0x0001,即EWIF=1
(3)结论
WWDG使能函数必须在WWDG清除提前唤醒中断标志位和开启窗口看门狗中断之前执行。