本帖最后由 chivalry-315158 于 2016-3-31 00:36 编辑
HAL库中有部分函数具有TimeOut参数,例如I2C的部分函数
- /******* Blocking mode: Polling */
- HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
- HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
- HAL_StatusTypeDef HAL_I2C_Slave_Transmit(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
- HAL_StatusTypeDef HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
- HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
- HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
- HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout);
复制代码
- static HAL_StatusTypeDef I2C_WaitOnFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, FlagStatus Status, uint32_t Timeout);
- static HAL_StatusTypeDef I2C_WaitOnMasterAddressFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, uint32_t Timeout);
- static HAL_StatusTypeDef I2C_WaitOnTXEFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout);
- static HAL_StatusTypeDef I2C_WaitOnRXNEFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout);
- static HAL_StatusTypeDef I2C_WaitOnSTOPFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout);
复制代码
其源码中关于TimeOut的检测方式如下:
- static HAL_StatusTypeDef I2C_WaitOnFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, FlagStatus Status, uint32_t Timeout)
- {
- uint32_t tickstart = 0;
- /* Get tick */
- tickstart = HAL_GetTick();
- /* Wait until flag is set */
- if(Status == RESET)
- {
- while(__HAL_I2C_GET_FLAG(hi2c, Flag) == RESET)
- {
- /* Check for the Timeout */
- if(Timeout != HAL_MAX_DELAY)
- {
- if((Timeout == 0)||((HAL_GetTick() - tickstart ) > Timeout))
- {
- hi2c->State= HAL_I2C_STATE_READY;
- /* Process Unlocked */
- __HAL_UNLOCK(hi2c);
- return HAL_TIMEOUT;
- }
- }
- }
- }
- else
复制代码 其中第16行代码:
HAL_GetTick的函数声明为:unsigned int HAL_GetTick(void)
其实现是系统Tick中断后一个unsigned int类型的变量自增,既然为unsigned int自增,那么当自增到0xFFFFFFFF后就会溢出,从0开始自增。
那么问题来了,上面那段代码,当Tick溢出为0后,变为:(0 - tickstart ) > Timeout ,一个负数会大于一个正数么? 系统Tick频率设置为1毫秒中断一次,那么0xFFFFFFFF毫秒大约为49.71026961805556天,也就是49天以后会产生第一次溢出,然后,就没有然后了。
Linux内核的jiffies变量与此类似,Linux内核用了几个简单的宏定义就完美的解决的问题,建议参考一下。
与此相同HAL层HAL_Delay函数也从在同样的问题,如果使用Delay只要溢出就要Delay上49天了:- __weak void HAL_Delay(__IO uint32_t Delay)
- {
- uint32_t tickstart = 0;
- tickstart = HAL_GetTick();
- while((HAL_GetTick() - tickstart) < Delay)
- {
- }
- }
复制代码 重定义的HAL_Delay函数,参考Linux内核的jiffies变量,解决Tick溢出问题- void HAL_Delay(__IO unsigned int Delay)
- {
- uint32_t timeout_tick = 0;
- uint32_t current_tick = 0;
- timeout_tick = HAL_GetTick()+Delay;
- do
- {
- current_tick = HAL_GetTick();
- }while((int32_t)current_tick - (int32_t)timeout_tick <0);
- }
复制代码
代码很简单,就是把unsigned int 类型强制转换为signed int做比较,详细原理请参考度娘。
希望ST能在下个版本的HAL库中修复这个问题。
为什么宏time_after(a,b)可以解决溢出回绕的问题
|
事实上,当tick溢出时,HAL_Delay()函数依然准确。
在HAL库的架构中__IO uint32_t uwTick;用于实现HAL_Delay()函数。
每1ms产生一次Tick中断,在Tick中断中uwTick++;
uint32_t HAL_GetTick()函数返回的是当前uwTick的值。
假设调用HAL_Delay(10);而执行tickstart = HAL_GetTick();后tickstart = 65530,
然后执行while((HAL_GetTick() - tickstart) < Delay){}
分析,当HAL_GetTick()返回0时的情况:
(HAL_GetTick() - tickstart) =(0-65530)
LZ可能认为这是个负数,此时会导致((HAL_GetTick() - tickstart) < Delay) = ((0 - 65530) < 10)=FALSE
事实并非如此,因为HAL_GetTick()的返回值、tickstart和Delay都是uint32_t 型数据,这样HAL_GetTick() - tickstart的结果不可能是负数!
正确的分析应该是这样((HAL_GetTick() - tickstart) < Delay) = ((0 - 65530) < 10)
=((65536 - 65530) < 10) = (6 < 10) = TRUE
LZ可以修改HAL_Delay()函数内容为
__weak void HAL_Delay(__IO uint32_t Delay)
{
uint32_t tickstart = 0;
uwTick = 65530; // 此处强制uwTick为65530
tickstart = HAL_GetTick();
while((HAL_GetTick() - tickstart) < Delay)
{
}
}
然后调用HAL_Delay(10);该函数会导致uwTick变量溢出,如果如LZ所想,HAL_Delay(10);的执行时间应该是49天,LZ可以亲自验证一下是不是。
虽然是很久以前的贴子,但是这个会误导人,65535=2^16离2^32次方远着那