分类: 嵌入式
2014-09-27 15:25:24
Task.c文件:
全局变量:
static xList pxReadyTasksLists[ configMAX_PRIORITIES ];
static xList xDelayedTaskList1;
PRIVILEGED_DATA static xList xDelayedTaskList2;
< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count.
PRIVILEGED_DATA static xList xPendingReadyList;
任务已经就绪,但是调度被禁止,暂时放到pending列表
PRIVILEGED_DATA static xList xSuspendedTaskList;
任务控制块结构:
typedef struct tskTaskControlBlock
{
volatile portSTACK_TYPE *pxTopOfStack;
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings;
#endif
xListItem xGenericListItem;
xListItem xEventListItem;
unsigned portBASE_TYPE uxPriority;
portSTACK_TYPE *pxStack;
signed char pcTaskName[ configMAX_TASK_NAME_LEN ];
#if ( portSTACK_GROWTH > 0 )
portSTACK_TYPE *pxEndOfStack;
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
unsigned portBASE_TYPE uxCriticalNesting;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
unsigned portBASE_TYPE uxTCBNumber;
#endif
#if ( configUSE_MUTEXES == 1 )
unsigned portBASE_TYPE uxBasePriority;
#endif
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
pdTASK_HOOK_CODE pxTaskTag;
#endif
#if ( configGENERATE_RUN_TIME_STATS == 1 )
unsigned long ulRunTimeCounter;
#endif
} tskTCB;
任务函数API:
主要分为以下几个:
任务创建:xTaskCreate()
删除:vTaskDelete()
优先级设置:vTaskPrioritySet()
任务挂起:vTaskSuspend()
任务唤醒:vTaskResume()
从中断函数唤醒:xTaskResumeFromISR()
禁止调度:vTaskSuspendAll()
允许调度:xTaskResumeAll()
一:任务创建。
signed portBASE_TYPE xTaskGenericCreate( pdTASK_CODE pxTaskCode, const signed char * const pcName, unsigned short usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pxCreatedTask, portSTACK_TYPE *puxStackBuffer, const xMemoryRegion * const xRegions )。
代码体概述:
1:分配TCB和任务堆栈
tskTCB * pxNewTCB;
pxNewTCB = prvAllocateTCBAndStack( usStackDepth, puxStackBuffer );:
2:栈顶指针赋值: pxTopOfStack = pxNewTCB->pxStack + ( usStackDepth - 1 );
3:初始化变量:名称,优先级,那两个列表项:xGenericItem,xEventItem
prvInitialiseTCBVariables( pxNewTCB, pcName, uxPriority, xRegions, usStackDepth );
初始化列表项
vListInitialiseItem( &( pxTCB->xGenericListItem ) );
vListInitialiseItem( &( pxTCB->xEventListItem ) );
设置所有者,以便通过xGenericListItem,xEventListItem找到盖TCB,及找到该任务。
listSET_LIST_ITEM_OWNER( &( pxTCB->xGenericListItem ), pxTCB );
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) uxPriority );
listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB );
4:判断是否是第一个任务
if( uxCurrentNumberOfTasks == ( unsigned portBASE_TYPE ) 1 )
{
pxCurrentTCB = pxNewTCB;
如果是第一个任务,就初始化上面那些全局变量链表。
prvInitialiseTaskLists();
}
else
{
if( xSchedulerRunning == pdFALSE )
{
if( pxCurrentTCB->uxPriority <= uxPriority )
{
pxCurrentTCB = pxNewTCB;
}
}
}
5:加入就绪链表
uxTaskNumber++;
prvAddTaskToReadyQueue( pxNewTCB );
下面列出函数体:
while( ( pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ) ) != NULL ) \
{ \
if( xTickCount < listGET_LIST_ITEM_VALUE( &( pxTCB->xGenericListItem ) ) ) \
{
break;
}
vListRemove( &( pxTCB->xGenericListItem ) );
if( pxTCB->xEventListItem.pvContainer )
{
vListRemove( &( pxTCB->xEventListItem ) );
}
if( pxTCB->uxPriority > uxTopReadyPriority )
{
uxTopReadyPriority = pxTCB->uxPriority;
}
vListInsertEnd( ( xList * ) &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB- >xGenericListItem ) );
}
6:看看是否发生调度。
if( xSchedulerRunning != pdFALSE )
{
如果系统已经在跑,而且这个新建立的任务优先级高,就发生调度。
if( pxCurrentTCB->uxPriority < uxPriority )
{
portYIELD_WITHIN_API();//牵涉到任务调度,会在----FreeRTOS调度----详细介绍。
}
}
二:任务删除
freertos的任务删除分两步完成,
第一步在vTaskDelete中 完成,FreeRTOS先把要删除的任务从就绪任务链表和事件等待链表中删除,然后把此任务添加到 任务删除链表(即那个xTasksWaitingTermination), 若删除的任务是当前运行任务,系统就执行任务调度函数.
第2步 则是在idle任务中完成,idle任务运 行时,检查xTasksWaitingTermination链表,如果有任务在这个表上,释放该任务占用的内存空间,并把该任务从任务删除链表中删除。
void vTaskDelete( xTaskHandle pxTaskToDelete )
{
tskTCB *pxTCB;
taskENTER_CRITICAL();
{
if( pxTaskToDelete == pxCurrentTCB )
{
pxTaskToDelete = NULL;
}
pxTCB = prvGetTCBFromHandle( pxTaskToDelete );
traceTASK_DELETE( pxTCB );
vListRemove( &( pxTCB->xGenericListItem ) );
if( pxTCB->xEventListItem.pvContainer )
{//如果是,则把它从事件等待链表中删除
vListRemove( &( pxTCB->xEventListItem ) );
}
//插入等待删除链表
vListInsertEnd( ( xList * ) &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) );
//增加uxTasksDeleted计 数
++uxTasksDeleted;
}
taskEXIT_CRITICAL();
if( xSchedulerRunning != pdFALSE )
{
if( ( void * ) pxTaskToDelete == NULL )
{
taskYIELD();//调度会在FreeRTOS调度章节中介绍。
}
}
}
Idle任务。
static portTASK_FUNCTION( prvIdleTask, pvParameters )
{
( void ) pvParameters;
for( ;; )
{
prvCheckTasksWaitingTermination();
…………………………….
这里prvCheckTasksWaitingTermination()就 是干这第2步的工作:每次调用它删除一个任务
static void prvCheckTasksWaitingTermination( void )
{
#if ( INCLUDE_vTaskDelete == 1 )
{
portBASE_TYPE xListIsEmpty;
if( uxTasksDeleted > ( unsigned portBASE_TYPE ) 0 )
{//禁止调度
vTaskSuspendAll();
xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); //打开调度
xTaskResumeAll();
if( !xListIsEmpty )
{
tskTCB *pxTCB;
//关中断
portENTER_CRITICAL();
{
pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( ( ( xList * ) &xTasksWaitingTermination ) );
vListRemove( &( pxTCB->xGenericListItem ) );
--uxCurrentNumberOfTasks;
--uxTasksDeleted;
}
portEXIT_CRITICAL();
//释放内存,删除tcb
prvDeleteTCB( pxTCB );
}
}
}
#endif
}
三:禁止调度,打开调度
调度器的禁止和打开
这是一种同步机制,比关中断要温和点。禁止调度由vTaskSuspendAll实现,打开调度由xTaskResumeAll实现。
void vTaskSuspendAll( void )
{
portENTER_CRITICAL();
++uxSchedulerSuspended;
portEXIT_CRITICAL();
}
这个很简单,系统维护一个计数uxSchedulerSuspended,当它大于0时 候表示禁止调度,等于0则打开调度(允许调度)。
signed portBASE_TYPE xTaskResumeAll( void )
{
register tskTCB *pxTCB;
signed portBASE_TYPE xAlreadyYielded = pdFALSE;
在禁止调度器件,如果ISR导致一个任务就绪,这个任务会放在xPendingReadyList中,一旦调度允许,必须把所有的xPendingzList中的任务移动到the appropriate ready list中。
portENTER_CRITICAL();
{//将计数减一
--uxSchedulerSuspended;
//如果等于0,则允许调度
if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE )
{
if( uxCurrentNumberOfTasks > ( unsigned portBASE_TYPE ) 0 )
{
portBASE_TYPE xYieldRequired = pdFALSE;
while( ( pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( ( ( xList * ) &xPendingReadyList ) ) ) != NULL )
{
vListRemove( &( pxTCB->xEventListItem ) );
vListRemove( &( pxTCB->xGenericListItem ) );
prvAddTaskToReadyQueue( pxTCB );
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
xYieldRequired = pdTRUE;
}
}
if( uxMissedTicks > ( unsigned portBASE_TYPE ) 0 )
{
while( uxMissedTicks > ( unsigned portBASE_TYPE ) 0 )
{
vTaskIncrementTick();
--uxMissedTicks;
}
#if configUSE_PREEMPTION == 1
{
xYieldRequired = pdTRUE;
}
#endif
}
if( ( xYieldRequired == pdTRUE ) || ( xMissedYield == pdTRUE ) )
{
xAlreadyYielded = pdTRUE;
xMissedYield = pdFALSE;
taskYIELD(); //又一次发生任务调度函数调用,任务调度章节会详细介绍。
}
}
}
}
portEXIT_CRITICAL();
return xAlreadyYielded;
}
四:任务的挂起与唤醒。
freertos的任务挂起与ucosii也不大一样。它把 所有挂起的任务加到xSuspendedTaskList中,而且一旦调用vTaskSuspend()函数挂起一个任务,该任务就将从所有它原先连入的链表中删除(包括就绪表,延时表和它等待的事件链表),也就是说,和 ucosii不同,一旦一个任务被挂起,它将取消先前它的延 时和对事件的等待。ucosii中是不同的,在ucosii里 面一个任务被挂起仅仅是把任务的状态或上一个OS_STAT_SUSPEND并从就绪表中删除,如果先前这个任务正在等待某事件,则并不取消等待。
//如果传进来的pxTaskToSuspend==NULL, 则表示挂起当前任务
void vTaskSuspend( xTaskHandle pxTaskToSuspend )
{
tskTCB *pxTCB;
taskENTER_CRITICAL();
{
if( pxTaskToSuspend == pxCurrentTCB )
{
pxTaskToSuspend = NULL;
}
pxTCB = prvGetTCBFromHandle( pxTaskToSuspend );
traceTASK_SUSPEND( pxTaskToSuspend );
vListRemove( &( pxTCB->xGenericListItem ) );
if( pxTCB->xEventListItem.pvContainer )
{
vListRemove( &( pxTCB->xEventListItem ) );
}
//插到xSuspendedTaskList
vListInsertEnd( ( xList * ) &xSuspendedTaskList, &( pxTCB->xGenericListItem ) );
}
taskEXIT_CRITICAL();
if( ( void * ) pxTaskToSuspend == NULL )
{
taskYIELD();///又是调度。
}
}
相反的唤醒就是把任务从xSuspendedTaskList中删除,加到对应的就绪链表中(根据任务的优先级),然后如果唤醒的任务 优先级高于当前任务优先级,则调度。
void vTaskResume( xTaskHandle pxTaskToResume )
{
tskTCB *pxTCB;
pxTCB = ( tskTCB * ) pxTaskToResume;
if( pxTCB != NULL )
{
taskENTER_CRITICAL();
{
if( prvIsTaskSuspended( pxTCB ) == pdTRUE )
{
traceTASK_RESUME( pxTCB );
vListRemove( &( pxTCB->xGenericListItem ) );
prvAddTaskToReadyQueue( pxTCB );
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
taskYIELD(); 调度。。。。。。。。
}
}
}
taskEXIT_CRITICAL();
}
}
从中断函数唤醒:
portBASE_TYPE xTaskResumeFromISR( xTaskHandle pxTaskToResume )
{
portBASE_TYPE xYieldRequired = pdFALSE;
tskTCB *pxTCB;
pxTCB = ( tskTCB * ) pxTaskToResume;
if( xTaskIsTaskSuspended( pxTCB ) == pdTRUE )
{
traceTASK_RESUME_FROM_ISR( pxTCB );
if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE )
{
xYieldRequired = ( pxTCB->uxPriority >= pxCurrentTCB->uxPriority );
vListRemove( &( pxTCB->xGenericListItem ) );
prvAddTaskToReadyQueue( pxTCB );
}
else
{
vListInsertEnd( ( xList * ) &( xPendingReadyList ), &( pxTCB->xEventListItem ) );
}
}
return xYieldRequired;
}
#endif
上面两种唤醒不大一样:
任务重唤醒:可以直接进行任务调度(如果唤醒的优先级比正在运行的优先级高)。
中断唤醒:最多可以把被唤醒的任务加入到就绪表或者pendinglist中,返回可以进行调度标志变量。
五:设置优先级
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask )
{
tskTCB *pxTCB;
unsigned portBASE_TYPE uxReturn;
portENTER_CRITICAL();
{
pxTCB = prvGetTCBFromHandle( pxTask );
uxReturn = pxTCB->uxPriority;
}
portEXIT_CRITICAL();
return uxReturn;
}
#endif
#if ( INCLUDE_vTaskPrioritySet == 1 )
void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority )
{
tskTCB *pxTCB;
unsigned portBASE_TYPE uxCurrentPriority, xYieldRequired = pdFALSE;
if( uxNewPriority >= configMAX_PRIORITIES )
{
uxNewPriority = configMAX_PRIORITIES - 1;
}
portENTER_CRITICAL();
{
if( pxTask == pxCurrentTCB )
{
pxTask = NULL;
}
pxTCB = prvGetTCBFromHandle( pxTask );
traceTASK_PRIORITY_SET( pxTask, uxNewPriority );
#if ( configUSE_MUTEXES == 1 )
{
uxCurrentPriority = pxTCB->uxBasePriority;
}
#else
{
uxCurrentPriority = pxTCB->uxPriority;
}
#endif
if( uxCurrentPriority != uxNewPriority )
{
if( uxNewPriority > uxCurrentPriority )
{
if( pxTask != NULL )
{
xYieldRequired = pdTRUE;
}
}
else if( pxTask == NULL )
{
xYieldRequired = pdTRUE;
}
#if ( configUSE_MUTEXES == 1 )
{
if( pxTCB->uxBasePriority == pxTCB->uxPriority )
{
pxTCB->uxPriority = uxNewPriority;
}
pxTCB->uxBasePriority = uxNewPriority;
}
#else
{
pxTCB->uxPriority = uxNewPriority;
}
#endif
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( configMAX_PRIORITIES - ( portTickType ) uxNewPriority ) );
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxCurrentPriority ] ), &( pxTCB->xGenericListItem ) ) )
{
vListRemove( &( pxTCB->xGenericListItem ) );
prvAddTaskToReadyQueue( pxTCB );
}
if( xYieldRequired == pdTRUE )
{
portYIELD_WITHIN_API();
}
}
}
portEXIT_CRITICAL();
}
#endif