【STM32CUBEMX】HAL 库的 Timeout=1 异常分析

背景

STM32CUBEMX 在生成的库函数,基本上都有输入参数 Timeout。
比如说:

HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout);

这几天就吃了这个 Timeout 的哑巴亏。。。怎么说~
我把这个 Timeout 设成 1 了,1 这个值是万万使不得的一个值,咱们分析下。

分析 Timeout = 1 为什么不能用

我们以HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout),先来看看 Timeout 这个值是怎么用的。

在进入 函数后,咱们可以看到这个语句:tickstart = HAL_GetTick();
语句的作用:获取进入函数时的时间。

提取HAL_GetTick的源码:

__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}

接儿,找到 uwTick 赋值的位置。

__weak void HAL_IncTick(void)
{
  uwTick++;
}

最后定位HAL_IncTick函数应用位置。

void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  HAL_SYSTICK_IRQHandler();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

咱们来屡下思路:
滴答时钟在每次中断时会更新 uwTick,以供各个HAL函数计算超时使用。

再回到HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)函数,看 tickstart 和 TImeout 是怎么用的。

        /* Timeout management */
        if((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick()-tickstart) >=  Timeout)))
        {
          errorcode = HAL_TIMEOUT;
          goto error;
        }

当 HAL_GetTick() - tickstart) >= Timeout 时,就会认为是 Timeout。这么看着没有任何问题。

那咱们假定 SysTick_Handler 的中断周期是 1ms。看下图,当tickstart在红色点上赋值,若操作在 SysTick_Handler 中断前未完成,SysTick_Handler 中断先执行了 uTick++,那么 HAL_GetTick() - tickstart) >= 1 就为真了。也就意味着,实际上,咱们的 Timeout 并不为 1 个 SysTick_Handler 中断周期。尤其是当 tickstart 赋值越向右靠近,越容易出现 Timeout,因此在调用HAL 库时,必须使 Timeout 设置大于1!
在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/XiuHua_Wu/article/details/83895857