全局变量:
static xList pxReadyTasksLists[configMAX_PRIORITIES ];
static xListxDelayedTaskList1;
PRIVILEGED_DATA static xListxDelayedTaskList2;
< Delayed tasks (two lists are used - one fordelays that have overflowed the current tick count.
PRIVILEGED_DATA static xListxPendingReadyList;
任务已经就绪,但是调度被禁止,暂时放到pending列表
PRIVILEGED_DATA static xListxSuspendedTaskList;
任务控制块结构:
typedef struct tskTaskControlBlock
{
volatileportSTACK_TYPE *pxTopOfStack;
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGSxMPUSettings;
#endif
xListItem xGenericListItem;
xListItem xEventListItem;
unsignedportBASE_TYPE uxPriority;
portSTACK_TYPE *pxStack;
signedchar pcTaskName[configMAX_TASK_NAME_LEN ];
#if ( portSTACK_GROWTH > 0)
portSTACK_TYPE*pxEndOfStack;
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
unsigned portBASE_TYPEuxCriticalNesting;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
unsignedportBASE_TYPE uxTCBNumber;
#endif
#if ( configUSE_MUTEXES == 1 )
unsigned portBASE_TYPEuxBasePriority;
#endif
#if ( configUSE_APPLICATION_TASK_TAG == 1)
pdTASK_HOOK_CODEpxTaskTag;
#endif
#if ( configGENERATE_RUN_TIME_STATS == 1)
unsigned longulRunTimeCounter;
#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 == ( unsignedportBASE_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
{
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 > ( unsignedportBASE_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中的任务移动到theappropriate ready list中。
portENTER_CRITICAL();
{//将计数减一
--uxSchedulerSuspended;
//如果等于0,则允许调度
if( uxSchedulerSuspended == ( unsignedportBASE_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 > ( unsignedportBASE_TYPE ) 0 )
{
while( uxMissedTicks > ( unsignedportBASE_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 ) );
}
}
returnxYieldRequired;
}
#endif
上面两种唤醒不大一样:
任务重唤醒:可以直接进行任务调度(如果唤醒的优先级比正在运行的优先级高)。
中断唤醒:最多可以把被唤醒的任务加入到就绪表或者pendinglist中,返回可以进行调度标志变量。
五:设置优先级
unsigned portBASE_TYPEuxTaskPriorityGet( xTaskHandle pxTask )
{
tskTCB *pxTCB;
unsigned portBASE_TYPE uxReturn;
portENTER_CRITICAL();
{
pxTCB =prvGetTCBFromHandle( pxTask );
uxReturn =pxTCB->uxPriority;
}
portEXIT_CRITICAL();
returnuxReturn;
}
#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;
}
}
elseif( 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