1. 程式人生 > >【STM32CUBEMX】HAL 庫的 Timeout=1 異常分析

【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! 在這裡插入圖片描述

在這裡插入圖片描述