vTaskDelay()与vTaskDelayUnitl()。vTaskDelay()是相对模式(相对延时函数),函数 vTaskDelayUntil()是绝对模式(绝对延时函数)。函数 vTaskDelay()在文件 tasks.c 中有定义,要使用此函数的话宏 INCLUDE_vTaskDelay 必须为 1
void vTaskDelay( const TickType_t xTicksToDelay )
{
BaseType_t xAlreadyYielded = pdFALSE;
//延时时间要大于 0。
if( xTicksToDelay > ( TickType_t ) 0U ) (1)
{
configASSERT( uxSchedulerSuspended == 0 );
vTaskSuspendAll(); (2)
{
traceTASK_DELAY();
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); (3)
}
xAlreadyYielded = xTaskResumeAll(); (4)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( xAlreadyYielded == pdFALSE ) (5)
{
portYIELD_WITHIN_API(); (6)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
(1)、延时时间由参数 xTicksToDelay 来确定,为要延时的时间节拍数,延时时间肯定要大于 0。否则的话相当于直接调用函数 portYIELD()进行任务切换。
(2)、调用函数 vTaskSuspendAll()挂起任务调度器。
(3) 、 调 用 函 数 prvAddCurrentTaskToDelayedList() 将 要 延 时 的 任 务 添 加 到 延 时 列 表pxDelayedTaskList 或 者 pxOverflowDelayedTaskList() 中 。 后 面 会 具 体 分 析 函 数prvAddCurrentTaskToDelayedList()。
(4)、调用函数 xTaskResumeAll()恢复任务调度器。
(5)、如果函数 xTaskResumeAll()没有进行任务调度的话那么在这里就得进行任务调度。
(6)、调用函数 portYIELD_WITHIN_API()进行一次任务调度。
在prvAddCurrentTaskToDelayedList中,加入到一个延时列表中,prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE )一个是要延时的时间,另一个是。把当前任务移除出就绪列表(就绪位清零)。代码具体功能:获取系统节拍之后,把要delay的任务从就绪状态列表中移除,就绪位清0,判断条件是否满足(所需要延时的时间是不是最大值)需要放到挂起列表中,也就是说这个任务直接相当于挂起了(此功能需要在宏中开启)。在不是最大值的时候,会定义一个变量timeToWake设置唤醒时间
TimeToWake = xConstTickCount+xTickToWait//进入的系统时间+要唤醒的时间
之后,根据列表的原理,设置列表项大小的数值为TimeToWake进行排序。会存在延时溢出列表(之前提到的是延时溢出列表)来存放溢出太多的时间
while (1)
{
funtion();
vTaskDelay(100)
}
如上函数的调用了function以后进行延时,延时的时间是100ms,但是整个任务的时间并不是固定的,因为function读取寄存器的时间是不确定的所以整个任务的时间也不能确定。相对性就是体现在这里。
函数 vTaskDelayUntil()会阻塞任务,阻塞时间是一个绝对时间,那些需要按照一定的频率运行的任务可以使用函数 vTaskDelayUntil()。
while (1)
{
funtion();
vTaskDelayUntil(100)
}
上述代码就可以延迟固定时间。当然也存在溢出的可能。
void TestTask( void * pvParameters )
{
TickType_t PreviousWakeTime;
//延时 50ms,但是函数 vTaskDelayUntil()的参数需要设置的是延时的节拍数,不能直接
//设置延时时间,因此使用函数 pdMS_TO_TICKS 将时间转换为节拍数。
const TickType_t TimeIncrement = pdMS_TO_TICKS( 50 );
PreviousWakeTime = xTaskGetTickCount(); //获取当前的系统节拍值
for( ;; )
{
/*************************任务主体*********************************/
//调用函数 vTaskDelayUntil 进行延时
vTaskDelayUntil( &PreviousWakeTime, TimeIncrement);
}
}
又名系统时钟节拍计数器。不管是什么系统,运行都需要有个系统时钟节拍,前面已经提到多次了,xTickCount 就是FreeRTOS 的系统时钟节拍计数器。每个滴答定时器中断中 xTickCount 就会加一,xTickCount 的具体操作过程是在函数 xTaskIncrementTick()中进行的。Systick直属于CortexM内核。实际作用类似于正常定时器
具体Systick细节在CortexM权威指南中有,重点关注前三个寄存器。
void delay_init()
{
u32 reload;
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟 HCLK
fac_us=SystemCoreClock/1000000; //不论是否使用OS,fac_us都需要使用
reload=SystemCoreClock/1000000; //每秒钟的计数次数 单位为M
reload*=1000000/configTICK_RATE_HZ; //根据configTICK_RATE_HZ设定溢出时间
//reload为24位寄存器,最大值:16777216,在72M下,约合0.233s左右
fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD=reload; //每1/configTICK_RATE_HZ秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
}
每个系统都需要一个系统运行时钟节拍。xTickCount就是FreeRtOS的系统时钟节拍。每产生一个节拍,滴答定时器中断+1。具体操作过程是在xTaskIncrementTick中进行的。
//systick中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();