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

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

今天遇到个很奇怪的问题, 不知道为什么, 单片机运行一会之后, 系统就没反应了, 经过调试发现, 系统卡在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电脑了. …

### 解决STM32HAL_Delay影响TIM2中断的问题 #### 问题分析 在STM32 HAL中,`HAL_Delay()`函数依赖于SysTick定时器来实现毫秒级延时[^1]。当调用`HAL_Delay()`时,程序会进入一个空循环等待,直到SysTick计数器的值达到指定的延时时间。如果在此期间发生其他高优先级中断(如TIM2中断),可能会导致中断处理被延迟或阻塞,从而影响系统的实时性。 #### 解决方案 为避免`HAL_Delay()`对TIM2中断的影响,可以采取以下几种方法: #### 方法一:调整中断优先级 通过调整SysTick和TIM2中断的优先级,确保TIM2中断的优先级高于SysTick中断。这样即使在`HAL_Delay()`执行期间,TIM2中断也能及时响应。 ```c // 设置TIM2中断优先级 HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0); // 主优先级0,子优先级0 HAL_NVIC_EnableIRQ(TIM2_IRQn); // 设置SysTick中断优先级 HAL_NVIC_SetPriority(SysTick_IRQn, 1, 0); // 主优先级1,子优先级0 ``` #### 方法二:使用自定义非阻塞延时函数 替代`HAL_Delay()`,使用基于定时器的非阻塞延时函数。这种方法不会阻塞CPU,允许其他中断正常运行。 ```c volatile uint32_t delay_start_tick; void delay_ms(uint32_t ms) { delay_start_tick = HAL_GetTick(); } uint8_t is_delay_finished(uint32_t ms) { return (HAL_GetTick() - delay_start_tick) >= ms; } ``` 在主循环中,可以这样使用: ```c delay_ms(100); // 延时100ms while (!is_delay_finished(100)) { // 在这里可以执行其他任务 } ``` #### 方法三:禁用SysTick中断 在需要精确控制中断优先级的情况下,可以临时禁用SysTick中断,以确保TIM2中断不受干扰。 ```c // 禁用SysTick中断 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 启用SysTick中断 SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; ``` 需要注意的是,禁用SysTick中断期间,`HAL_Delay()`将无法正常工作,因此应尽量缩短禁用时间。 #### 方法四:使用独立的定时器实现延时 通过配置一个独立的定时器(如TIM3)来实现延时功能,避免使用SysTick。这种方式完全独立于SysTick,不会对其它中断造成影响。 ```c // 初始化TIM3用于延时 void MX_TIM3_Init(void) { TIM_HandleTypeDef htim3; htim3.Instance = TIM3; htim3.Init.Prescaler = 7200 - 1; // 72MHz/(7200) = 10kHz htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 0xFFFF; // 最大计数值 if (HAL_TIM_Base_Init(&htim3) != HAL_OK) { Error_Handler(); } HAL_TIM_Base_Start(&htim3); } // 非阻塞延时函数 void delay_tim3(uint32_t ms) { __HAL_TIM_SET_COUNTER(&htim3, 0); // 清零计数器 while (__HAL_TIM_GET_COUNTER(&htim3) < (ms * 10)) { // 每tick 0.1ms // 允许其他任务运行 } } ``` #### 性能对比 - **`HAL_Delay()`**:简单易用,但可能阻塞中断。 - **自定义非阻塞延时**:灵活性高,不影响其他中断[^4]。 - **独立定时器延时**:完全独立于SysTick,适合复杂系统[^2]。 #### 注意事项 - 调整中断优先级时需谨慎,避免影响系统稳定性。 - 自定义延时函数需确保计数器溢出处理正确。 - 使用独立定时器时,需合理分配资源,避免冲突。 ### 示例代码 以下是一个完整的示例,展示如何使用TIM3实现非阻塞延时,同时确保TIM2中断正常运行。 ```c #include "stm32f1xx_hal.h" TIM_HandleTypeDef htim2, htim3; void MX_TIM2_Init(void) { // 配置TIM2为100ms中断 htim2.Instance = TIM2; htim2.Init.Prescaler = 7200 - 1; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 1000 - 1; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } HAL_TIM_Base_Start_IT(&htim2); } void MX_TIM3_Init(void) { // 配置TIM3为延时定时器 htim3.Instance = TIM3; htim3.Init.Prescaler = 7200 - 1; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 0xFFFF; if (HAL_TIM_Base_Init(&htim3) != HAL_OK) { Error_Handler(); } HAL_TIM_Base_Start(&htim3); } void delay_tim3(uint32_t ms) { __HAL_TIM_SET_COUNTER(&htim3, 0); while (__HAL_TIM_GET_COUNTER(&htim3) < (ms * 10)) { // 非阻塞延时 } } int main(void) { HAL_Init(); SystemClock_Config(); MX_TIM2_Init(); MX_TIM3_Init(); while (1) { delay_tim3(100); // 延时100ms // 执行其他任务 } } ```
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值