[STM32F0] 学习笔记+STM32F0系列的STM32Cube固件例程 IWDG

[复制链接]
1590|1
 楼主| slotg 发表于 2019-12-29 22:23 | 显示全部楼层 |阅读模式
本帖最后由 slotg 于 2019-12-29 23:48 编辑

学习笔记+适用于STM32F0系列的STM32Cube固件例程
学习笔记+STM32F0系列的STM32Cube固件例程 DMA


程序中有时候会因为程序上的 bug 或是外部杂讯的影响让程序跑飞或是卡死,因此大部份的 MCU 芯片里面都会有 WATCHDOG 看门狗的设计,看门狗就是一个倒数的计数器,当计数器减为 0 时就会对 MCU 做复位 (RESET) 动作,在 STM32F0 里面有2种看门狗分别是 IWDG 与 WWDG。IWDG 是 Independent watchdog 独立看门狗,它使用了内部 LSI 做为时钟驱动,即使主时钟停止仍可以工作。

P04.JPG


IWDG_Reset

在固件库 Projects\STM32F030R8-Nucleo\Examples\IWDG\IWDG_Reset 里面的这一个例程:

P01.JPG

我们从 main() 回圈开始了解一下流程:
  1.   HAL_Init();
  2.   
  3.   /* Configure the system clock to 48 MHz */
  4.   SystemClock_Config();

  5.   /* Configure LED2 */
  6.   BSP_LED_Init(LED2);

在基本的设定之后设定了 使用者按键 为中断触发的模式:
  1.   /* Configure User push-button */
  2.   BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);

由于 IWDG 是使用内部 LSI 时钟驱动,频率约为 40kHz,而这个频率的误差是很大的,为了准确的掌握喂狗时间因此使用 TIM14 去量测 LSI 时钟的真实频率再来设定计数参数:

P02.JPG

例程中设定 IWDG 的计数时间为 1秒钟,也就是程序要在 1秒钟的周期内去重新设定计数值(喂狗),否则当计数值减为0之后就会复位 MCU。
  1.   /*##-2- Get the LSI frequency: TIM14 is used to measure the LSI frequency ###*/
  2.   uwLsiFreq = GetLSIFrequency();
  3.   
  4.   /*##-3- Configure & Start the IWDG peripheral #########################################*/
  5.   /* Set counter reload value to obtain 1 sec. IWDG TimeOut.
  6.      IWDG counter clock Frequency = uwLsiFreq
  7.      Set Prescaler to 32 (IWDG_PRESCALER_32)
  8.      Timeout Period = (Reload Counter Value * 32) / uwLsiFreq
  9.      So Set Reload Counter Value = (1 * uwLsiFreq) / 32 */
  10.   IwdgHandle.Instance = IWDG;
  11.   IwdgHandle.Init.Prescaler = IWDG_PRESCALER_32;
  12.   IwdgHandle.Init.Reload = (uwLsiFreq / 32);
  13.   IwdgHandle.Init.Window = IWDG_WINDOW_DISABLE;

由于 IWDG 的计数时间是 1秒钟,因此在 while(1) 主回圈中我们以 990ms 周期喂狗一次,并反转 LD2 状态。
  1.   /* Infinite loop */
  2.   while (1)
  3.   {
  4.     /* Toggle LED2 */
  5.     BSP_LED_Toggle(LED2);

  6.     /* Insert 990 ms delay */
  7.     HAL_Delay(990);

  8.     /* Refresh IWDG: reload counter */
  9.     if(HAL_IWDG_Refresh(&IwdgHandle) != HAL_OK)
  10.     {
  11.       /* Refresh Error */
  12.       Error_Handler();
  13.     }
  14.   }

当我们按下使用者键时会产生按键中断,中断函数里关闭了 LD2 显示,并对地址 0x00040001 写入数据 0xFF,由于这个地址的写入动作是无效的因此会产生一个 Hardfault 错误事件。
  1. void EXTI4_15_IRQHandler(void)
  2. {
  3.   /* Failure is generated by user, turn LED2 off */
  4.   BSP_LED_Off(LED2);

  5.   /* As the following address is invalid (not mapped), a Hardfault exception
  6.   will be generated with an infinite loop and when the IWDG counter falls to 0
  7.   the IWDG reset occurs */
  8.   *(__IO uint32_t *) 0x00040001 = 0xFF;
  9. }

在 Hardfault 响应函数中我们放了一个无限回圈让程序回不去。
  1. void HardFault_Handler(void)
  2. {
  3.   /* Go to infinite loop when Hard Fault exception occurs */
  4.   while (1)
  5.   {
  6.   }
  7. }

由于程序回不去了因此主回圈就没有办法喂狗,所以在约 1秒钟内 IWDG 会复位 MCU,而在 main() 开始运行的代码中有一个判断语句判断程序的启动原因是否是因为 IWDG 所产生的?是的话点亮 LD2 4秒钟。
  1.   /*##-1- Check if the system has resumed from IWDG reset ####################*/
  2.   if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET)
  3.   {
  4.     /* IWDGRST flag set: Turn LED2 on */
  5.     BSP_LED_On(LED2);

  6.     /* Insert 4s delay */
  7.     HAL_Delay(4000);

  8.     /* Notification done: Turn LED2 off */
  9.     BSP_LED_Off(LED2);
  10.   }

所以这个例程的动作是平常时 LD2 会以 0.99秒的周期反转状态,当按下使用者按键后 LD2 立即关闭,约 1秒时间内 LD2 亮起 4秒的时间,然后又回到以 0.99秒的周期闪烁。








 楼主| slotg 发表于 2019-12-29 22:23 | 显示全部楼层
本帖最后由 slotg 于 2019-12-29 22:32 编辑

另一个 IWDG 例程:

IWDG_WindowMode

在固件库 Projects\STM32F030R8-Nucleo\Examples\IWDG\IWDG_WindowMode

P03.JPG

与上一个例程不同的是这一个例程演示了 IWDG 的窗口看门狗功能,在这个例程中看门狗计数器设定了窗口,有了 2 个计数时间,主回圈必须在这 2 个计数时间内喂狗否则 MCU 会被复位,太晚喂狗不行,太早喂狗也不行。
  1.   /*##-2- Configure & Start the IWDG peripheral ##############################*/
  2.   /* Set counter reload value to obtain 762ms IWDG TimeOut.
  3.      Counter Reload Value = (LsiFreq(Hz) * Timeout(ms)) / (prescaler * 1000)                        
  4.   */
  5.   IwdgHandle.Instance = IWDG;
  6.   IwdgHandle.Init.Prescaler = IWDG_PRESCALER_16;
  7.   IwdgHandle.Init.Reload = (40000 * 762) / (16 * 1000); /* 762 ms */
  8.   IwdgHandle.Init.Window = (40000 * 400) / (16 * 1000); /* 400 ms */

例程中设定了喂狗最长时间是 762ms,窗口 400ms,所以喂狗时间是在 362ms 到 762ms 之间,并使用了一个做为喂狗周期的变数 WaitingDelay 并设定为 450,因此在 while(1) 主回圈中是以 450ms 周期喂狗一次,并反转 LD2 状态。
  1.   /* Initial delay will be 450 ms in order to be inside the window */
  2.   WaitingDelay = 450;

  3.   /* Infinite loop */
  4.   while (1)
  5.   {
  6.     /* Toggle LED2 */
  7.     BSP_LED_Toggle(LED2);

  8.     HAL_Delay(WaitingDelay);

  9.     /* Refresh IWDG: reload counter */
  10.     if(HAL_IWDG_Refresh(&IwdgHandle) != HAL_OK)
  11.     {
  12.       /* Refresh Error */
  13.       Error_Handler();
  14.     }
  15.   }

当我们按下使用者按键时会产生按键中断,中断回调函数里将 WaitingDelay 变数设定为 200。
  1. void EXTI4_15_IRQHandler(void)
  2. {
  3.   HAL_GPIO_EXTI_IRQHandler(USER_BUTTON_PIN);
  4. }
  1. void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
  2. {
  3.   /* EXTI line interrupt detected */
  4.   if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  5.   {
  6.     __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
  7.     HAL_GPIO_EXTI_Callback(GPIO_Pin);
  8.   }
  9. }
  1. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  2. {
  3.   if(GPIO_Pin == USER_BUTTON_PIN)
  4.   {
  5.     /* waiting 200 ms to be above window value on next refresh */
  6.     WaitingDelay = 200;
  7.   }
  8. }

200ms 少于最短喂狗时间,因此 IWDG 复位 MCU。


您需要登录后才可以回帖 登录 | 注册

本版积分规则

38

主题

1177

帖子

6

粉丝
快速回复 在线客服 返回列表 返回顶部