OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 16528|回复: 11

讨论:HAL库的HAL_Delay函数bug问题

[复制链接]

342

主题

1462

帖子

12

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
8024
金钱
8024
注册时间
2015-10-15
在线时间
2902 小时
发表于 2016-5-25 12:28:23 | 显示全部楼层 |阅读模式
10金钱
STM32的HAL库提供了一个HAL_Delay函数,使用Systick中断来实现,实现过程如下:

1.首先是一个Systick中断配置函数SysTick_Config,该函数实现的是Systick中断每隔ticks中断一次。。。。。
[mw_shl_code=c,true]__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  {
    return (1UL);                                                   /* Reload value impossible */
  }

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  return (0UL);                                                     /* Function successful */
}[/mw_shl_code]

2.其次依次是:HAL_SYSTICK_Config函数和HAL_InitTick函数。。。。
HAL_InitTick函数调用之后,实际上就是配置每隔1ms中断一次,最终调用的是SysTick_Config函数进行配置。而这个函数是在HAL_Init函数中调用,在系统初始化之后会调用。


[mw_shl_code=c,true]uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
   return SysTick_Config(TicksNumb);
}

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /*Configure the SysTick to have interrupt in 1ms time basis*/
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  /*Configure the SysTick IRQ priority */
  HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0);

  /* Return function status */
  return HAL_OK;
}[/mw_shl_code]

3.经过前面2步骤,开启了Systick定时器,每隔1ms中断一次。。。
而中断服务函数和相关逻辑函数是这样的:
[mw_shl_code=c,true]void SysTick_Handler(void)
{
  HAL_IncTick();
}

static __IO uint32_t uwTick;

__weak void HAL_IncTick(void)
{
  uwTick++;
}


__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}[/mw_shl_code]
定义了一个32位的全局变量uwTick,每次中断(1ms),变量uwTick增加1.。。。


4.最后HAL_Delay函数是这样的:
[mw_shl_code=c,true]__weak void HAL_Delay(__IO uint32_t Delay)
{
  uint32_t tickstart = 0;
  tickstart = HAL_GetTick();
  while((HAL_GetTick() - tickstart) < Delay)
  {
  }
}[/mw_shl_code]

通过HAL_GetTick();函数获取此时uwTick的值赋给tickstart,然后使用while循环不断调用HAL_GetTick() 函数获取当前uwTick值减掉tickstart,差值就是定时的ms数。。。


问题来了,如果32位的uwTick越界了怎么办? 32位uwTick的值,每1ms增加1,最大只有几十天就越界了。。。。
例如,如果还有1s就越界,如果此时此刻,我们调用HAL_Delay延时10s,那么肯定就乱套了。。。。
ST不应该会犯如此大的错误吧?不知道是我看少了,还是确实是一个Bug,大家讨论一下。。。。




最佳答案

查看完整内容[请看2#楼]

这不是bug,程序没有问题 用减法是不会出错的,能回避这个问题。 接楼上的说法,0-4294967290的结果并不是-4294967290,而是6! 因为这是在32bit环境下,-4294967290是一个至少33bit的数据,只会截取低32位 (如图,只看bit[31:0]会有什么神奇的现象?) 所以这里写成:是有讲究的 倘若写成: 就会出严重问题
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

6

主题

1097

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3571
金钱
3571
注册时间
2014-12-2
在线时间
365 小时
发表于 2016-5-25 12:28:24 | 显示全部楼层
本帖最后由 xkwy 于 2016-5-25 13:56 编辑

这不是bug,程序没有问题

用减法是不会出错的,能回避这个问题。

接楼上的说法,0-4294967290的结果并不是-4294967290,而是6!

因为这是在32bit环境下,-4294967290是一个至少33bit的数据,只会截取低32位
c.png
(如图,只看bit[31:0]会有什么神奇的现象?)


所以这里写成:
while((HAL_GetTick() - tickstart) < Delay)
是有讲究的


倘若写成:
while(HAL_GetTick() < (tickstart+Delay))

就会出严重问题
坚决不用寄存器,拒绝重复造轮子。
回复

使用道具 举报

342

主题

1462

帖子

12

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
8024
金钱
8024
注册时间
2015-10-15
在线时间
2902 小时
 楼主| 发表于 2016-5-25 12:29:47 | 显示全部楼层
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

3

主题

347

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
2082
金钱
2082
注册时间
2014-12-19
在线时间
711 小时
发表于 2016-5-25 12:54:35 | 显示全部楼层
本帖最后由 lvehe 于 2016-5-25 13:17 编辑

不是BUG,简单的写了个程序验证,模拟即将越界时的情况。打印情况如下:
0, 4294967291
1, 4294967292
2, 4294967293
3, 4294967294
4, 4294967295
5, 0
6, 1
7, 2
8, 3
9, 4
退出循环

[mw_shl_code=c,true]
uint32_t uwTick, tickstart, tmp;
void delay(uint32_t ms)
{
        tickstart = 4294967296 - 5;
        uwTick = tickstart;
        while ((tmp = (uwTick - tickstart)) < ms)
        {
                printf("%u, %u\r\n", tmp, uwTick);
                uwTick++;
        }
}
int main(void)
{
        delay(10);
        getchar();
}[/mw_shl_code]



回复

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
发表于 2016-5-25 13:03:32 | 显示全部楼层
本帖最后由 八度空间 于 2016-5-25 13:08 编辑

看这个函数
__weak void HAL_Delay(__IO uint32_t Delay)
{
  uint32_t tickstart = 0;
  tickstart = HAL_GetTick();
  while((HAL_GetTick() - tickstart) < Delay)
  {
  }
}
先获取上次的值,要减去上次的值,这样延时是对的,关于溢出,在32溢出后从0开始的,将战舰V3的跑马灯上面做实验,代码如下
1234.png
上述代码没做溢出处理,串口打印的结果是
123.png
溢出后从0开始,那么问题来了,打个比方说uwTick当前的值是4294967290,我要延时10ms,就是说uwTick这边变量还有5ms就溢出了,溢出后从0开始重新计数,HAL_GetTick() - tickstart这计算就成了-4294967290了,结果还是小于Delay,等待再次加到10的时候才退出循环,这样就变长了


看到前面面有一个__weak在修饰,有人这么解释http://blog.csdn.net/kissmonx/article/details/21622187
还是有点不懂什么意思,大家可以研究一下

现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复

使用道具 举报

6

主题

1097

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3571
金钱
3571
注册时间
2014-12-2
在线时间
365 小时
发表于 2016-5-25 14:00:19 | 显示全部楼层
八度空间 发表于 2016-5-25 13:03
看这个函数
__weak void HAL_Delay(__IO uint32_t Delay)
{

在8位环境下,

1-255的结果不是想象中的-254,而是2
坚决不用寄存器,拒绝重复造轮子。
回复

使用道具 举报

342

主题

1462

帖子

12

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
8024
金钱
8024
注册时间
2015-10-15
在线时间
2902 小时
 楼主| 发表于 2016-5-25 15:58:11 | 显示全部楼层
xkwy 发表于 2016-5-25 05:51
这不是bug,程序没有问题

用减法是不会出错的,能回避这个问题。

YES,你回答得非常对。。。稍微深入研究一下代码就可以了。。。

没有问题。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

3

主题

548

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1383
金钱
1383
注册时间
2015-2-3
在线时间
197 小时
发表于 2016-5-25 18:11:35 | 显示全部楼层
八度空间 发表于 2016-5-25 13:03
看这个函数
__weak void HAL_Delay(__IO uint32_t Delay)
{

__weak的意思大概就是存在感弱,可以被替代吧
比如HAL中定义了__weak void HAL_Delay(),这时如果用户在别的地方又定义了另外一个void HAL_Delay()函数,这时编译器就忽视__weak void HAL_Delay()而以用户定义的void HAL_Delay()为准
回复

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
发表于 2016-5-25 19:47:45 | 显示全部楼层
yyx112358 发表于 2016-5-25 18:11
__weak的意思大概就是存在感弱,可以被替代吧
比如HAL中定义了__weak void HAL_Delay(),这时如果用户在 ...

嗯,各种查找也解决了这个疑惑
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
发表于 2016-5-25 19:48:14 | 显示全部楼层
xkwy 发表于 2016-5-25 14:00
在8位环境下,

1-255的结果不是想象中的-254,而是2

还真没细究这个现象
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复

使用道具 举报

9

主题

43

帖子

0

精华

初级会员

Rank: 2

积分
184
金钱
184
注册时间
2016-7-18
在线时间
67 小时
发表于 2017-5-18 10:01:08 | 显示全部楼层
有意思,原来HAL库的延时函数这么讲究
回复

使用道具 举报

23

主题

59

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
242
金钱
242
注册时间
2015-11-11
在线时间
15 小时
发表于 2020-7-1 15:03:32 | 显示全部楼层
原来如此。刚开始学习,也考虑到这个问题。还是数制问题。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-1 03:21

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表