FreeRTOS中Systick和HAL时基的使用详解

目录

分析

举个例子:

正确的做法:

 总结


我们首先需要知道,使用了FreeRTOS会强制使用systick作为自己的心跳,这个os_tick的优先级是最低的,它主要的作用就是OS任务调度,时间片查询等工作。

在图中,这里设置Timebase是HAL库所使用的基本时钟hal_tick,如果这里也设置成systick,优先级最低,那么在高优先级(优先级高于systick) 中断服务函数中调用HAL_Delay()就会导致错误

当然,我们也经常强调不要在中断中使用延时!不要在中断中使用延时!不要在中断中使用延时!(中断一般是快进快出的)

HAL_Delay()函数在库中是基于所选择的Timebase时钟源来实现的:

/**
  * @brief This function provides minimum delay (in milliseconds) based 
  *        on variable incremented.
  * @note In the default implementation , SysTick timer is the source of time base.
  *       It is used to generate interrupts at regular time intervals where uwTick
  *       is incremented.
  * @note This function is declared as __weak to be overwritten in case of other
  *       implementations in user file.
  * @param Delay specifies the delay time length, in milliseconds.
  * @retval None
  */
__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((HAL_GetTick() - tickstart) < wait)
  {
  }
}

所以,我们设置成其他定时器,当然建议使用基本定时器TIM6或者TIM7,它的优先级会默认设置成最高。

分析

stm32+freertos之所以要用到两套timebase(一是freertos的心跳(os_tick)二是更底层的与freertos的api无关的hal心跳(hal_tick),譬如systick(这个是强制的os_tick)和timer(譬如timer1、hal_tick));

是因为freertos强制使用systick作为自己的心跳时钟,且systick的优先级被强制设置为最低,如果HAL的心跳也使用systick,也即systick=hal_tick,那么在某些情况下将产生我们意想不到的结果。

举个例子:

systick的中断优先级为最低的5,我们有个中断int_a的优先级为4,显然int_a的优先级要高于systick,systick的中断是不能够抢占int_a的。

考虑以下情形:int_a的中断服务函数里调用了HAL_Delay(10),等待10个tick(HAL_Delay)内部是一个while循环,不断的读取当前的hal_tick来判断是否到时间,hal_tick随着时间的流失是不断增长的,由于只有一个timebase源(systick),hal_tick增长的任务也就交给systick的中断服务了,由于systick不能够抢占int_a,就导致以下的情况:systick无法处理自己的中断服务函数,hal_tick也就不会增长,int_a的HAL_Delay(10)也就永远等不到自己返回的日子,其结果就是int_a中断无法返回,任何优先级低于此中断的中断都无法得到服务,当然也包括os的全部调度,因为os的调度依赖于systick中断。

正确的做法:

timer(譬如timer1)产生HAL心跳,systick产生freertos的心跳,HAL是不依赖于OS API的一套硬件操作接口。

os_tick是如何工作的:

当配置了两套时基时,systick作为os的心跳,它的优先级应该配置为最低,这是因为:

systick的中断服务,主要处理一些os层面的东西,譬如查看是否有blocked的任务已经就绪,然后执行任务切换等工作,作为一个实时操作系统为了保持实时性不打搅其他的中断服务,stm32cubemx里会强制systick的优先级为最低。

hal_tick是如何工作的:

为了保证HAL时基的可靠,防止出现上面讲到的一直忙等待问题,强制设定它的优先级为最高,我们使用基本定时器TIM6或者TIM7作为HAL时基,一般是1ms,HAL_Delay()函数就是使用的这个时基。

 总结

防止在高优先级(优先级高于Systick) 中断服务函数中调用HAL_Delay(),导致中断服务忙等待,这样任何优先级低于该中断的中断都得不到服务(低中断不能打断高中断),当然这里包括os的全部调度。

物联沃分享整理
物联沃-IOTWORD物联网 » FreeRTOS中Systick和HAL时基的使用详解

发表评论