STM32 使用HAL库,HAL_Delay()会卡死, 程序一直卡在 HAL_GetTick( ) 函数中(已解决)

文章讲述了作者在STM32F103C8T6单片机项目中遇到系统卡在HAL_Delay中的问题,通过追踪发现是定时器TIM3的停止和开启导致的uwTickFreq变为0,最终通过重写HAL_IncTick函数解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天遇到个很奇怪的问题, 不知道为什么, 单片机运行一会之后, 系统就没反应了, 经过调试发现, 系统卡在HAL_Delay()中了.
之前也遇到过这个问题后来把HAL_Delay 去掉了. 然后发现不行, 还是得有它.不然发串口数据发的太快会乱掉. 得慢点发.

然后调试到HAL_Delay()方法的内部发现 HAL_GetTick( )函数一直返回

__weak void HAL_Delay(uint32_t Delay)
{
  uint32_t tickstart = HAL_GetTick();
  uint32_t wait = Delay;

  /* Add a freq to guarantee minimum wait */
  if (wait < HAL_MAX_DELAY)
  {
    wait += (uint32_t)(uwTickFreq);
  }
	
  //卡在这个while函数里
  while ((HAL_GetTick() - tickstart) < wait)
  {
  }
}

再跟踪发现 HAL_GetTick() - tickstart 永远等于0
这就很奇怪了.
于是再深入看一下HAL_GetTick()函数. 代码如下

__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}

也就是说, uwTick 不再更新了. 数值永远是某个固定数值.
那uwTick 到底是怎么更新的呢?
最后发现
在这里更新的.

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

调试发现 uwTickFreq ===0
也就是说, uwTick 不再增加了.
所以导致外层的while死循环了.
那么uwTickFreq又是什么时候变成0的呢?
我跟踪了很久也没有拦截到uwTickFreq 是什么时候更新的. keil工具也拦截不到是什么时候更新的.
最后索性, 给uwTickFreq 一个默认值. 只要发现 uwTickFreq 为0了.
就给它个默认值., 然后就把代码改成了下面这样的函数.
由于源代码函数是__weak 的, 也就是说, 可以被覆盖的.
于是我在自己的main.c 文件代码中重新写了一下. 如下:

void HAL_IncTick(void)
{
	if(uwTickFreq==0)
	{
		uwTickFreq = HAL_TICK_FREQ_DEFAULT;
	}
  uwTick += uwTickFreq;
}

如果不在乎延迟时间的精准度, 可以考虑.写成下面的这种, 更快一点

void HAL_IncTick(void)
{ 
  uwTick += HAL_TICK_FREQ_DEFAULT;
} 

后来经过调试, 我猜测是因为定时器的停止和开启导致的.
具体中间的原因就不再深入细究了. 有时间精力的朋友可以再追踪下去.
找到原因欢迎分享.

我用的是STM32F103C8T6 , 其中的定时器TIM3用来驱动ADC的采样频率.
在循环中不停的停止和开启Tim3. 我估计是因为这个原因导致的. uwTickFreq 重新计算.

而且我这里做了自动计算Tim3的自动重载计数值. 估计是跟它有关.
具体的不再深入探究了.
延迟时间就写死吧…

后来我换了个定时器, 不再重置TIM定时器的PSC, 然后就好了. .

昨天电脑坏了重装系统, 然后小半年的代码都没了. …我操…该死的Dell电脑, 恶心的要死, 故意搞的不能重装系统. 不能从u盘启动, 以后再也不买dell电脑了. …

### 回答1: 这可能是由于HAL_Delay(1000)阻塞了STM32的系统时钟,在while循环中等待时间过程中系统时钟停止工作,无法计时,造成住的现象。去掉延时函数后,系统时钟可以持续运转,计时器也可以正常工作,因此代码不会卡住。建议使用其他方法进行延时,例如使用定时器或者外部中断等方式。 ### 回答2: 代码在运行时,在while((HAL_GetTick() - tickstart) < wait)住的原因是由于HAL_Delay函数在延迟1秒时,会使程序暂停运行1秒钟,此时HAL_GetTick()函数记录的时间不会更新。而在while循环中,一直判断(HAL_GetTick() - tickstart) < wait,如果HAL_GetTick()的值不变,那么循环条件始终成立,导致程序一直在这个循环中。 当将HAL_Delay(1000); // 延迟1秒注释掉后,代码不再住的原因是没有延迟,程序可以正常运行,执行完while循环中的语句后,程序继续执行下一行代码,而不会进入到延迟函数内部,因此程序不再住。 解决这个问题的办法是可以在循环中添加一些延迟,以便HAL_GetTick()的值可以及时更新。例如在循环内添加一个短暂的延迟,比如HAL_Delay(1);,使得程序每次循环都会有一小段时间的休眠,从而让HAL_GetTick()有机会更新时间。这样可以保证程序不会一直在while循环中。 另外,可以考虑使用HAL_Delay函数的替代方法,比如使用HAL_Delay的底层实现函数,或者使用其他的延迟函数来代替。这样可以避免在延迟函数中导致HAL_GetTick()的时间不更新的问题。 ### 回答3: 在这种情况下,代码在while((HAL_GetTick() - tickstart) < wait)陷入无限循环。HAL_GetTick()函数返回系统启动后的毫秒数,tickstart是一个变量,表示代码开始运行时的时间,wait是一个变量,表示期望的延迟时间。 当执行到while((HAL_GetTick() - tickstart) < wait)时,代码会不断地比较系统当前时间与代码开始时间的差值,直到这个差值大于等于wait时,才会跳出循环。 在这个问题中,当将代码中的HAL_Delay(1000); // 延迟1秒注释掉后,while循环中没有延迟操作,导致循环非常快速地执行。在一秒内,很可能无法满足差值大于等于wait的条件,因此代码会一直处于循环状态,没有机会跳出循环。 而当加上延迟操作时,每次循环执行时会延迟1秒钟,这样while循环中的差值会随着时间的流逝逐渐增大,最终达到大于等于wait的条件,才会跳出循环,使代码继续执行。 因此,从逻辑上来说,将代码中的延迟操作注释掉会导致while循环一直无法满足跳出条件,从而使代码住。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值