目录
我们首先需要知道,使用了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的全部调度。