【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!