STM32F103ZE单片机在WWDG窗口看门狗的EWI中断中喂狗导致系统复位的原因及解决办法(中断函数重入问题)

转载自https://blog.csdn.net/ZLK1214/article/details/78308058

程序开启了WWDG的Early wakeup(EWI)中断,在中断中喂狗,但系统不停地复位。

HCLK=72MHz, PCLK1=4.5MHz, PCLK2=72MHz

程序如下:

 

 
  1. #include <stdio.h>
  2. #include <stm32f10x.h>
  3.  
  4. int fputc(int ch, FILE *fp)
  5. {
  6. if (fp == stdout)
  7. {
  8. if (ch == '\n')
  9. {
  10. while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
  11. USART_SendData(USART1, '\r');
  12. }
  13. while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
  14. USART_SendData(USART1, ch);
  15. }
  16. return ch;
  17. }
  18.  
  19. int main(void)
  20. {
  21. GPIO_InitTypeDef gpio;
  22. USART_InitTypeDef usart;
  23.  
  24. RCC_PCLK1Config(RCC_HCLK_Div16);
  25. RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // 打开WWDG的时钟后, 计数器就开始空转
  26. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
  27.  
  28. gpio.GPIO_Mode = GPIO_Mode_AF_PP;
  29. gpio.GPIO_Pin = GPIO_Pin_9;
  30. gpio.GPIO_Speed = GPIO_Speed_50MHz;
  31. GPIO_Init(GPIOA, &gpio);
  32.  
  33. USART_StructInit(&usart);
  34. usart.USART_BaudRate = 115200;
  35. usart.USART_Mode = USART_Mode_Tx;
  36. USART_Init(USART1, &usart);
  37. USART_Cmd(USART1, ENABLE);
  38.  
  39. if (RCC_GetFlagStatus(RCC_FLAG_PINRST) == SET)
  40. printf("PIN reset!\n");
  41. if (RCC_GetFlagStatus(RCC_FLAG_PORRST) == SET)
  42. printf("POR reset!\n");
  43. if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET)
  44. printf("WWDG reset!\n");
  45. RCC_ClearFlag();
  46.  
  47. WWDG_Enable(94); // 启动看门狗并在没有窗口值的情况下喂狗
  48. WWDG_SetPrescaler(WWDG_Prescaler_8);
  49. WWDG_SetWindowValue(74); // 设置窗口值
  50. WWDG_EnableIT();
  51. NVIC_EnableIRQ(WWDG_IRQn);
  52.  
  53. while (1);
  54. }
  55.  
  56. void WWDG_IRQHandler(void)
  57. {
  58. WWDG_ClearFlag();
  59. WWDG_SetCounter(94);
  60. }

不过只要去掉RCC_PCLK1Config(RCC_HCLK_Div16)这句话,让PCLK1回到默认的36MHz,系统就不再复位了。

 

 

于是配置DBGMCU_Config(DBGMCU_WWDG_STOP, ENABLE),设置调试时WWDG暂停。然后打开ST-Link调试,发现系统复位前WWDG_IRQHandler中断函数连续执行了两次,第一次执行时T=0x40,正常喂狗,紧接着又再次重入了中断,T=0x5e,这个时候喂狗就引发了系统复位,因为此时计数值T>窗口值W。

问题的原因找到了,这是因为程序的执行速度(HCLK)太快,而中断标志位EWIF的清除需要一定的时间(PCLK1),前者的速度是后者的16倍,虽然在中断函数里面调用了清除中断标志位的函数,但是到了中断执行完毕后EWIF都还未来得及清除,所以中断又再次重入了,第二次喂狗就引发了系统复位。

解决办法就是,清除标志位后等待一下标志位真正清除,然后再喂狗,退出中断。保证中断只执行一次。

 

 
  1. void WWDG_IRQHandler(void)

  2. {

  3. WWDG_ClearFlag();

  4. while (WWDG_GetFlagStatus() == SET); // 等待中断标志位真正清除, 防止中断重入

  5. WWDG_SetCounter(94);

  6. }

当HCLK=2*PCLK2时不需要等待,因为标志位的清除速度能够跟得上中断的执行速度。

 

 

程序一开始的时候就在RCC中打开了WWDG外设的时钟,这时WDGA=0,窗口看门狗未启用但处于空转状态,EWIF的置位规律如下:

 

T=127
T=126
T=125
...
T=66
T=65
T=64 *** EWIF置位, 手动清除标志位
T=63
...
T=2
T=1
T=0 *** EWIF置位, 手动清除标志位
T=127
T=126
...
T=66
T=65
T=64 *** EWIF置位, 手动清除标志位
T=63
...

在一个计数周期内EWIF会自动置位两次。但在窗口看门狗启动之前(WDGA=0),即使打开了EWI中断,也不会进入中断函数WWDG_IRQHandler执行。只有WDGA=1且EWIF=1时才会触发中断。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值