FreeRTOS FreeRTOS--时间管理

编辑:
发布时间: 2020-12-15 00:37:57
分享:

http://blog.sina.com.cn/s/blog_5f0bed160100tqr3.html

2011

时间管理包括两个方面:系统节拍的维护,产生;以及任务延时管理。下面分别讨论下。

时钟节拍

操作系统总是需要个时钟节拍的,这个需要硬件支持。freertos同样需要一个timetick产生器,通常是用处理器的硬件定时器来实现这个功能。它周期性的产生定时中断,所谓的时钟节拍管理的核心就是这个定时中断的服务程序。freertos的时钟节拍isr中除去保存现场,灰度现场这些事情外,核心的工作就是调用vTaskIncrementTick函数。

比如CortexM3STM32中处理器的移植代码中,对应的时钟节拍isr如下:

void xPortSysTickHandler

{

unsigned long ulDummy;

#ifconfigUSE_PREEMPTION == 1

* =portNVIC_PENDSVSET;

#endif

ulDummy =portSET_INTERRUPT_MASK_FROM_ISR;

{

vTaskIncrementTick;

}

portCLEAR_INTERRUPT_MASK_FROM_ISR;

}

vTaskIncrementTick函数主要做两件事情:维护系统时间;处理那些延时的任务,如果延时到期,则唤醒任务。

inline void vTaskIncrementTick

{

if pdFALSE )

{

++xTickCount;

//如果加完后等于0,则说明溢出了

if 0 )

{

xList *pxTemp;

pxTemp = pxDelayedTaskList;

pxDelayedTaskList = pxOverflowDelayedTaskList;

pxOverflowDelayedTaskList = pxTemp;

xNumOfOverflows++;

}

prvCheckDelayedTasks;

}

else

{//如果调度器被禁止,则我们把丢失的时钟节拍记录在全局变量uxMissedTicks中

++uxMissedTicks;

#if

{

extern void vApplicationTickHook;

vApplicationTickHook;

}

#endif

}

#if

{

extern void vApplicationTickHook;

if

{

vApplicationTickHook;

}

}

#endif

traceTASK_INCREMENT_TICK;

}

上一篇“freertos任务管理分析”中已经说过,freertos定义了如下两个链表:

static xList xDelayedTaskList1;

static xList xDelayedTaskList2;

另外定义了两个指针:

static xList * volatile pxDelayedTaskList;

static xList * volatilepxOverflowDelayedTaskList;

这两个双向链表把所有需要延时的任务挂在上面,而pxDelayedTaskList则永远指向当前正在使用那个链表。而pxOverflowDelayedTaskList指向的链表包含的任务都是延时到期的绝对时间发生溢出的那些任务。所谓延时绝对时间就是任务相延时的时间加上当前系统时间的值。

如果一个时钟节拍中断发生,在vTaskIncrementTick函数中发现xTickCount溢出了,那么我们就需要交换pxDelayedTaskList和pxOverflowDelayedTaskList。

任务延时管理

任务可能需要延时,两种情况,一种是任务被vTaskDelay或者vTaskDelayUntil延时,另外一种情况就是任务等待事件时候指定了timeout

延时管理离不开上面的时钟节拍isr。下面先看vTaskDelay函数。它的功能就是把任务从就绪链表中拿下,加到xDelayedTaskList1或者xDelayedTaskList2上。

//参数:xTicksToDelay—延时的节拍数

void vTaskDelay

{

portTickType xTimeToWake;

signed portBASE_TYPE xAlreadyYielded = pdFALSE;

if0 )

{//禁止调度器

vTaskSuspendAll;

{

traceTASK_DELAY;

xTimeToWake = xTickCount + xTicksToDelay;

vListRemove & );

listSET_LIST_ITEM_VALUE, xTimeToWake );

//判断xTimeToWake是否溢出

if

{

vListInsert pxOverflowDelayedTaskList, & );

}

else

{

vListInsert pxDelayedTaskList, & );

}

}

//打开调度器

xAlreadyYielded = xTaskResumeAll;

}

if

{

taskYIELD;

}

}

这里巧妙的设计是vListInsert,我们调用它将任务插到pxDelayedTaskList/pxOverflowDelayedTaskList中,但是我们并不是随便找个地方插入的,而是按照延时的到期时间的大小,按照从小到大的次序插入到链表的,这样vTaskIncrementTick里调用的prvCheckDelayedTasks只需要检查pxDelayedTaskList链表的第一个任务的唤醒时间是否已到,而不需要扫描整个链表,这个效率是O的,非常高。

vTaskIncrementTickàprvCheckDelayedTasks

#defineprvCheckDelayedTasks

{

register tskTCB*pxTCB;

//得到延时任务链表上的第一个任务

whilelistGET_OWNER_OF_HEAD_ENTRY ) != NULL)

{

//如果当前时间小于第一个任务的唤醒时间,则说明也不需要做

if ))

{

break;

}

//否则,则第一个任务到期,把它从延时任务链表上拿下

vListRemove);

if

{

vListRemove);

}

//加到就绪链表

prvAddTaskToReadyQueue;

//然后再检查延时任务链表上的下一个任务是否到期。这是因为有可能多个任务的

}

}

FreeRTOS提供了另外一个延时函数vTaskDelayUntil,这个函数与vTaskDelay的不同之处在于:一个周期性任务可以利用它可以保证一个固定的常数执行频率,而vTaskDelay无法保证。原因如下:vTaskDelay指定的延时量是相对于当前调用vTaskDelay这个函数的时刻而言的,比如你传给vTaskDelay函数参数xTicksToDelay=100,意味这你这个任务将以调用vTaskDelay这个函数的时刻为零点,延时100个时钟节拍后被唤醒。

因为从任务被唤醒开始执行到下一次调用vTaskDelay这段时间不是个确定的常数,所以没办法通过调用vTaskDelay来实现这个功能。然而vTaskDelayUtil不一样,它指定的延时节拍数是相对于前一次调用vTaskDelayUtil而言的,这样就能保证比较精确的两次调用之间的时间是个确定的常数。

void vTaskDelayUntil

{

portTickType xTimeToWake;

portBASE_TYPE xAlreadyYielded, xShouldDelay =pdFALSE;

//禁止调度

vTaskSuspendAll;

{

xTimeToWake = *pxPreviousWakeTime +xTimeIncrement;

//自从上次调用vTaskDelayUtil,到现在xTickCount已经溢出

if

{

if || )

{

xShouldDelay = pdTRUE;

}

}

*pxPreviousWakeTime = xTimeToWake;

if

{//如果确实需要延时,下面就和vTaskDelay一样做延时

traceTASK_DELAY_UNTIL;

vListRemove & );

listSET_LIST_ITEM_VALUE, xTimeToWake );

if

{

vListInsert pxOverflowDelayedTaskList, & );

}

else

{

vListInsert pxDelayedTaskList, & );

}

}

}

xAlreadyYielded = xTaskResumeAll;

if

{

taskYIELD;

}

}

相关阅读
热门精选
孩子 皮肤