打印
[STM32F0]

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

[复制链接]
1342|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 做为时钟驱动,即使主时钟停止仍可以工作。




IWDG_Reset

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



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

  /* Configure LED2 */
  BSP_LED_Init(LED2);

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

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



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

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

    /* Insert 990 ms delay */
    HAL_Delay(990);

    /* Refresh IWDG: reload counter */
    if(HAL_IWDG_Refresh(&IwdgHandle) != HAL_OK)
    {
      /* Refresh Error */
      Error_Handler();
    }
  }

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

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

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

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

    /* Insert 4s delay */
    HAL_Delay(4000);

    /* Notification done: Turn LED2 off */
    BSP_LED_Off(LED2);
  }

所以这个例程的动作是平常时 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



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

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

  /* Infinite loop */
  while (1)
  {
    /* Toggle LED2 */
    BSP_LED_Toggle(LED2);

    HAL_Delay(WaitingDelay);

    /* Refresh IWDG: reload counter */
    if(HAL_IWDG_Refresh(&IwdgHandle) != HAL_OK)
    {
      /* Refresh Error */
      Error_Handler();
    }
  }

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

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


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

38

主题

1177

帖子

6

粉丝