你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

关于HAL库中超时函数中的tick溢出后产生的问题

[复制链接]
chivalry-315158 提问时间:2016-3-30 23:38 /
本帖最后由 chivalry-315158 于 2016-3-31 00:36 编辑

HAL库中有部分函数具有TimeOut参数,例如I2C的部分函数

  1. /******* Blocking mode: Polling */
  2. HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
  3. HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
  4. HAL_StatusTypeDef HAL_I2C_Slave_Transmit(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
  5. HAL_StatusTypeDef HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
  6. 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);
  7. 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);
  8. HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout);
复制代码

  1. static HAL_StatusTypeDef I2C_WaitOnFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, FlagStatus Status, uint32_t Timeout);
  2. static HAL_StatusTypeDef I2C_WaitOnMasterAddressFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, uint32_t Timeout);
  3. static HAL_StatusTypeDef I2C_WaitOnTXEFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout);
  4. static HAL_StatusTypeDef I2C_WaitOnRXNEFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout);
  5. static HAL_StatusTypeDef I2C_WaitOnSTOPFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout);
复制代码


其源码中关于TimeOut的检测方式如下:

  1. static HAL_StatusTypeDef I2C_WaitOnFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, FlagStatus Status, uint32_t Timeout)
  2. {
  3.   uint32_t tickstart = 0;

  4.   /* Get tick */
  5.   tickstart = HAL_GetTick();

  6.   /* Wait until flag is set */
  7.   if(Status == RESET)
  8.   {
  9.     while(__HAL_I2C_GET_FLAG(hi2c, Flag) == RESET)
  10.     {
  11.       /* Check for the Timeout */
  12.       if(Timeout != HAL_MAX_DELAY)
  13.       {
  14.         if((Timeout == 0)||((HAL_GetTick() - tickstart ) > Timeout))
  15.         {
  16.           hi2c->State= HAL_I2C_STATE_READY;

  17.           /* Process Unlocked */
  18.           __HAL_UNLOCK(hi2c);

  19.           return HAL_TIMEOUT;
  20.         }
  21.       }
  22.     }
  23.   }
  24.   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天了:
  1. __weak void HAL_Delay(__IO uint32_t Delay)
  2. {
  3.   uint32_t tickstart = 0;
  4.   tickstart = HAL_GetTick();
  5.   while((HAL_GetTick() - tickstart) < Delay)
  6.   {
  7.   }
  8. }
复制代码
重定义的HAL_Delay函数,参考Linux内核的jiffies变量,解决Tick溢出问题
  1. void HAL_Delay(__IO unsigned int Delay)
  2. {
  3.         uint32_t timeout_tick = 0;
  4.         uint32_t current_tick = 0;
  5.         timeout_tick = HAL_GetTick()+Delay;
  6.         do
  7.         {
  8.                 current_tick = HAL_GetTick();
  9.         }while((int32_t)current_tick - (int32_t)timeout_tick <0);
  10. }
复制代码

代码很简单,就是把unsigned int 类型强制转换为signed int做比较,详细原理请参考度娘。

希望ST能在下个版本的HAL库中修复这个问题。


为什么宏time_after(a,b)可以解决溢出回绕的问题





收藏 评论4 发布时间:2016-3-30 23:38

举报

4个回答
adlu 回答时间:2016-3-31 10:04:41
LZ多虑了。
事实上,当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
adlu 回答时间:2016-3-31 10:09:51
本帖最后由 adlu 于 2016-3-31 10:50 编辑

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可以亲自验证一下是不是。
wu1169668869 回答时间:2017-1-12 01:50:08
来学习一下,楼上分析的在理
zlfxia 回答时间:2020-3-7 20:56:44
adlu 发表于 2016-3-31 10:09
LZ可以修改HAL_Delay()函数内容为
__weak void HAL_Delay(__IO uint32_t Delay)
{

虽然是很久以前的贴子,但是这个会误导人,65535=2^16离2^32次方远着那

所属标签

相似问题

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版