HAL_Delay() 陷入死循环

HAL_Delay() stuck in a infinite loop

我受困于 HAL_Delay() 函数。当我调用此函数 HAL_Delay() 时,控件陷入无限循环。 在搜索问题时,我发现了这个

http://www.openstm32.org/forumthread2145#threadId2146

在这个特定的评论中,我引用了 "There is problem with linker file please use the one attached. You need to map two banks of memory separately so first SRAM1 96K and then SRAM2 of 32K. I think this should be reported as bug in CubeMX as it generates bad linker file." 并且有两个扩展名为 .ld 的文件。

我正在寻找的是如何在我的项目中使用这些文件或处理此问题的任何其他更好的选择。

PS。我正在使用 stm32l476 探索板、Cube Mx 5.0.0 和 Attolic True Studio。

编辑

我的项目有一个 RS485 通信,我从那里获取数据,我有两个任务处理该数据,将其显示在 MAX7219 显示屏上并使用 sim800 gsm 模块将其发送到互联网。

控件卡住的代码。请注意,此函数仅在执行 GSM 任务时调用。

void vMyDelay(uint16_t ms)
{
    HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", strlen("\r\n"), 1000);
    HAL_UART_Transmit(&huart2, (uint8_t*)"In Delay", strlen("In Delay"), 1000);
    HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", strlen("\r\n"), 1000);
    for (int i = 0; i < ms; i++ )       HAL_Delay(1);
    HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", strlen("\r\n"), 1000);
    HAL_UART_Transmit(&huart2, (uint8_t*)"Out Delay", strlen("Out Delay"), 1000);
    HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", strlen("\r\n"), 1000);
}

此函数在终端上写入In Delay,但不显示Out Delay。但我还有一个定时器,它每 2 秒调用一次以显示 MAX72219 上的数据。

下面的代码是

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", strlen("\r\n"), 1000);
    HAL_UART_Transmit(&huart2, (uint8_t*)"HAL_TIM_PeriodElapsedCallback()", strlen("vRS485_CollectInverterData()"), 1000);
    HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", strlen("\r\n"), 1000);
    if (htim->Instance == htim3.Instance)
    {
        vMax7219_ClearDisplay();
        switch (uiMax7219Index)
        {
            case 0: vMax7219_SendNumberToString(ucFreq7219,1);      break;
            case 1: vMax7219_SendNumberToString(ucInVolt7219,1);    break;
            case 2: vMax7219_SendNumberToString(ucOutVolt7219,1);   break;
            case 3: vMax7219_SendNumberToString(ucOutCurr7219,1);   break;
            case 4: vMax7219_SendNumberToString(ucLoadSpd7219,1);   break;
            case 5: vMax7219_SendNumberToString(ucOutPwr7219,1);    break;
        }
        uiMax7219Index++;
        if (uiMax7219Index > 5) uiMax7219Index = 0;
    }
}

控件卡住后,这个函数总是在2秒后触发。因此得出的结论是,控件以某种方式卡在 HAL_Delay() 中。

小鬼东西

问题每次都会发生,但没有具体时间,即控制可能会在 5 分钟、10 分钟或 15 分钟后卡住。它不会因特定功能而卡住。功能可能不同。即有时它可能会被函数名称卡住 getIMEI() 或者有时它可能是我 get service provider

所有延迟和超时 HAL 函数都依赖于 SysTick 处理程序中递增的计数器。如果您在另一个中断中使用这些函数中的任何一个,则必须确保 SysTick 中断的优先级高于该中断。否则,永远不会调用 SysTick 处理程序,您将陷入无限循环,因为计数器永远不会增加。

修复:

总结:
增加 SysTick_Handler NVIC 优先级(通过减小其 NVIC 数值,范围为 0 到 15)。

详情:
@P__J__ 在他的回答 中所说的是正确的,我也怀疑这是你的问题。要修复它,您需要使您的 SysTick 中断具有 NVIC(嵌套向量中断控制器) 优先级 高于 任何其他进行可能依赖于系统节拍递增的 HAL 调用的中断。 这包括所有具有超时的 HAL 调用,例如,以及 HAL 延迟。较高的 NVIC 优先级意味着您必须将其设置为 较低 数值,因为在默认配置下,STM32 芯片的最高 NVIC 优先级为 0,最低为 15。

要在 STM32CubeMX 5 中设置 NVIC 优先级,请转到 Pinout & Configuration --> System Core -->(单击微小的 up/down 箭头进入显示 NVIC 的页面),然后单击NVIC --> 将“抢占优先级”值降低到低于(高于)任何其他依赖 HAL 调用的 ISR。

这是截图。请注意,您也可以通过单击“Pinout view”旁边的“System view”按钮进入此屏幕,然后单击“System Core”部分下的“NVIC”。

截图:

关于 HAL_IncTick(); 的更多信息:

您将从“stm32f4xx_it.c”文件中看到,SysTick_Handler ISR 调用 HAL_IncTick();:

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
  HAL_IncTick();
}

如果您按住 Ctrl 并单击它(至少在系统 Workbench/Eclipse 中)跳转到 HAL_IncTick() 的实现,您将看到以下内容,它在评论中提供了一些额外的见解:

/**
  * @brief This function is called to increment  a global variable "uwTick"
  *        used as application time base.
  * @note In the default implementation, this variable is incremented each 1ms
  *       in Systick ISR.
  * @note This function is declared as __weak to be overwritten in case of other 
  *      implementations in user file.
  * @retval None
  */
__weak void HAL_IncTick(void)
{
  uwTick++;
}

这个 HAL_IncTick() 函数可以在文件“...STM32Cube_FW_F4_V1.19.0/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c”中找到,该文件还包含上面的 HAL_InitTick() 函数HAL_IncTick()。它的评论很有见地:

/**
  * @brief This function configures the source of the time base.
  *        The time source is configured  to have 1ms time base with a dedicated 
  *        Tick interrupt priority.
  * @note This function is called  automatically at the beginning of program after
  *       reset by HAL_Init() or at any time when clock is reconfigured  by HAL_RCC_ClockConfig().
  * @note In the default implementation, SysTick timer is the source of time base. 
  *       It is used to generate interrupts at regular time intervals. 
  *       Care must be taken if HAL_Delay() is called from a peripheral ISR process, 
  *       The SysTick interrupt must have higher priority (numerically lower)
  *       than the peripheral interrupt. Otherwise the caller ISR process will be blocked.
  *       The function is declared as __weak  to be overwritten  in case of other
  *       implementation  in user file.
  * @param TickPriority Tick interrupt priority.
  * @retval HAL status
  */
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /* Configure the SysTick to have interrupt in 1ms time basis*/
  if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
  {
    return HAL_ERROR;
  }

  /* Configure the SysTick IRQ priority */
  if (TickPriority < (1UL << __NVIC_PRIO_BITS))
  {
    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
    uwTickPrio = TickPriority;
  }
  else
  {
    return HAL_ERROR;
  }

  /* Return function status */
  return HAL_OK;
}

特别注意:上面写着的部分:

Care must be taken if HAL_Delay() is called from a peripheral ISR process,
The SysTick interrupt must have higher priority (numerically lower)
than the peripheral interrupt. Otherwise the caller ISR process will be blocked.

这正是我学到这个的地方。

确保有时会跳转代码并查看 ST 的 HAL 源代码本身内部的函数和文档,以找到像这样隐藏的洞察力。当然,除了参考以下核心文档外,还要这样做:

您的芯片的主要 STM32 文档,按优先顺序排列(最重要的在前):

  1. 参考手册:RM0351
  2. 数据表:DS10198
  3. UM1725 - Description of STM32F4 HAL and LL drivers
  4. 编程手册:PM0214

这些和其他重要手册很容易在 ST 的网站 (https://www.st.com/en/microcontrollers/stm32l476vg.html) 上找到,或者更方便:在 STM32CubeMX 中通过帮助 --> 文档和资源(快捷方式: Alt + D).