背景
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!