STM HAL 与 FreeRTOS,是/否/也许?

bas*_*bas 4 microcontroller stm32 freertos

我花了一段时间才让我的程序稳定运行。我的程序运行时遇到硬故障。我正在兜圈子。

我的项目:

  • Nucleo F446ze (STM32F446ze)
  • 连接到 uart2 的 LTE 调制解调器
  • 我的电脑连接到 uart3(仅用于日志记录)。
  • FreeRTOS 使用其 STM 端口从 git 下载
  • 从 git 下载 LWIP 2.1.0

一些 freertos 配置

  • 在免费 rtos 中启用 configASSERT
  • configCHECK_FOR_STACK_OVERFLOW 设置为 2
  • configUSE_MALLOC_FAILED_HOOK 设置为 1
  • configTOTAL_HEAP_SIZE设置为30k(当我查询剩余堆大小时还剩下10k)
  • INCLUDE_uxTaskGetStackHighWaterMark 设置为 1(所有任务都在堆栈限制内)
  • SysTick 专用于 FreeRTOS。我在 1khz 上使用 TIM6 来增加 HAL 刻度。
  • 所有 NVIC 中断均设置为 5 或更高,并且再次启用 configASSERT,因此非常确定“中断管理”已涵盖。

并使用定义将免费 rtos 中断处理程序映射到 CMSIS

#define vPortSVCHandler    SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
Run Code Online (Sandbox Code Playgroud)

我的程序按顺序执行以下操作:

  • 设置时钟和外设
  • 启用中断
  • 创建“StartLwIP”任务
  • 启动 FreeRTOS 调度程序

然后“StartLwIP”执行以下操作:

  • 通过 uart2 向 LTE 调制解调器发送命令以启用数据模式
  • 初始化 LwIP 堆栈(与对等方协商 ppp)
  • 开始新的“测试”任务

“测试”任务的作用是:

  • 打开与 Internet 上的 TCP 服务器的连接
  • 发送一个消息
  • 关闭套接字
  • vTaskDelay [100|10|-]
  • 重复

当我使用 vTaskDelay(100) 时,程序可以毫无问题地运行几个小时(运行过夜,没有问题)。

当我使用 vTaskDelay(10) 时,程序会运行一段时间(1 分钟到 5 分钟之间)。然后它会崩溃并挂在硬故障处理程序中。

当我删除 vTaskDelay(这将是首选解决方案)时,它会崩溃得更快。同样,它会有所不同,但在几秒到一分钟之间。

我 99% 确定问题与堆/堆栈无关。高水位线和堆消耗看起来非常好。甚至无法接近堆/堆栈之外。

LWIP 的内存管理让我有些困惑,但由于我只是不断地打开和关闭连接,所以我不敢相信 LWIP 中的 PBUF 已经用完了。无论如何,我扩大了数字。

我挣扎了几周,最终开始怀疑 STM HAL。然后我偶然发现了__HAL_LOCK外围库(在我的例子中是 uart)。例如在HAL_UART_Transmit_IT

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  /* Check that a Tx process is not already ongoing */
  if (huart->gState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return HAL_ERROR;
    }

    /* Process Locked */
    __HAL_LOCK(huart);                         <<<<====== 

    huart->pTxBuffPtr = pData;
    huart->TxXferSize = Size;
    huart->TxXferCount = Size;

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->gState = HAL_UART_STATE_BUSY_TX;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);                       <<<<====== 

    /* Enable the UART Transmit data register empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_TXE);

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}
Run Code Online (Sandbox Code Playgroud)

当我查看锁宏的定义时,我有点担心:

#if (USE_RTOS == 1U)
  /* Reserved for future use */
  #error "USE_RTOS should be 0 in the current HAL release"
#else
  #define __HAL_LOCK(__HANDLE__)                                           \
Run Code Online (Sandbox Code Playgroud)

我读过几个关于此的主题。例如这里这里。我还可以阅读很多主题,认为锁定机制实现得很差并且根本不是线程安全的。有趣的是,即使没有 RTOS,但使用中断也会是一个潜在的问题。

我下载了STMCube最新版本来检查现在是否可以解决这个问题。但一切仍处于同一个状态。STM HAL 似乎对其 USE_RTOS 宏没有做太多事情。

在我的程序中,我使用不同的任务来读取和写入同一 uart 实例。LWIP TCP 线程将发送数据,而 LWIP RX 线程将不断从 uart 读取数据。我的串口以中断模式接收数据(逐字节传递到环形缓冲区)。

最后我的问题:

  1. 有没有可能这个锁定机制就是我的硬故障的根本原因?我试图找到遇到同样问题的人,但找不到可以证实这一点的“证据”。所以也许“可怕的锁定机制”不是最好的实现,但不是我的问题的根本原因。

  2. 是否需要采取“步骤”来从硬故障中获取更多详细信息?我真的很想找到有问题的代码行。我发现这个页面解释了如何继续,但我不知道如何获取电脑(我正在使用VScode,我可以在 while(1) 循环中中断,但然后呢...?)。

它总是在这里崩溃:

在此输入图像描述

HardFault_Handler
prvPortStartFirstTask
xPortStartScheduler
Run Code Online (Sandbox Code Playgroud)

抱歉问了这么长的问题,但我至少想彻底地回答,并希望有人可以确认一些事情,或者甚至可以帮助我朝正确的方向克服这个问题......

提前谢谢了!

P__*_*J__ 5

任务切换时出现HF的常见原因有3种:

  1. 在 ISR 中使用非 ISR 功能。
  2. 堆栈溢出(在 freeRTOS 配置中启用堆栈溢出检查)
  3. 错误的中断优先级 freeRTOS 文档中对这三个中断都有很好的解释。

我个人更喜欢在调度程序启动时在任务中进行初始化。它可以防止许多非常难以跟踪的代码问题。

我使用 freeRTOS 运行 HAL、LWIP、不同类型的网络连接,没有任何问题。