FreeRTOS的任务创建由函数xTaskCreate()完成。函数源码如下:
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
TCB_t *pxNewTCB;
BaseType_t xReturn;
StackType_t *pxStack;
pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); (1)
if( pxStack != NULL )
{
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); (2)
if( pxNewTCB != NULL )
{
/* Store the stack location in the TCB. */
pxNewTCB->pxStack = pxStack; (3)
}
else
{
/* The stack cannot be used as the TCB was not created. Free
it again. */
vPortFree( pxStack ); (4)
}
}
else
{
pxNewTCB = NULL;
}
if( pxNewTCB != NULL )
{
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; (5)
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); (6)
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS; (7)
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
函数prvInitialiseNewTask()用于完成对任务的初始化。源码如下:
static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask,
TCB_t *pxNewTCB,
const MemoryRegion_t * const xRegions ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
StackType_t *pxTopOfStack;
UBaseType_t x;
#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) )
{
/* Fill the stack with a known value to assist debugging. */
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); (1)
}
#endif
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); (2)
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
{
pxNewTCB->pcTaskName[ x ] = pcName[ x ]; (3)
if( pcName[ x ] == 0x00 )
{
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; (4)
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) (5)
{
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxNewTCB->uxPriority = uxPriority; (6)
#if ( configUSE_MUTEXES == 1 ) (7)
{
pxNewTCB->uxBasePriority = uxPriority;
pxNewTCB->uxMutexesHeld = 0;
}
#endif
vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); (8)
vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); (9)
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); (10)
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); (11)
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); (12)
#if ( portCRITICAL_NESTING_IN_TCB == 1 ) //使能临界区嵌套
{
pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
}
#endif /* portCRITICAL_NESTING_IN_TCB */
#if ( configUSE_APPLICATION_TASK_TAG == 1 ) //使能任务标签功能
{
pxNewTCB->pxTaskTag = NULL;
}
#endif /* configUSE_APPLICATION_TASK_TAG */
#if ( configGENERATE_RUN_TIME_STATS == 1 ) //使能时间统计功能
{
pxNewTCB->ulRunTimeCounter = 0UL;
}
#endif /* configGENERATE_RUN_TIME_STATS */
#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )
{
for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
{
pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL; (12)
}
}
#endif
#if ( configUSE_TASK_NOTIFICATIONS == 1 )//使能任务通知功能
{
pxNewTCB->ulNotifiedValue = 0;
pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )//使能NEWLIB
{
/* Initialise this task's Newlib reent structure. */
_REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
}
#endif
#if( INCLUDE_xTaskAbortDelay == 1 )//使能函数INCLUDE_xTaskAbortDelay()
{
pxNewTCB->ucDelayAborted = pdFALSE;
}
#endif
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); (13)
if( ( void * ) pxCreatedTask != NULL )
{
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB; (14)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
任务初始化函数中会对任务堆栈初始化,这个过程通过调用函数pxPortInitialiseStack()来完成。下面就是该函数的源码:
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
pxTopOfStack--;
*pxTopOfStack = portINITIAL_XPSR; /* xPSR */ (1)
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; (2)
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */ (3)
/* Save code space by skipping register initialisation. */
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ (4)
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ (5)
/* A save method is being used that requires each task to maintain its
own exec return value. */
pxTopOfStack--;
*pxTopOfStack = portINITIAL_EXEC_RETURN; (6)
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ (7)
return pxTopOfStack;
}
堆栈是用来在进行上下文切换的时候保存现场的,一般在新创建好一个堆栈以后会对其先进行初始化处理,即对Cortex-M内核的某些寄存器赋初值。这些初值就保存在任务堆栈中,保存的顺序按照:XPSR、R15(PC)、R14(LR)、R12、R3 ~ RO、R11 ~ R14。
(1)、寄存器xPSR值为portINITIAL_XPSR,其值为0×01000000。xPSR是Cortex-M4的一个内核寄存器,叫做程序状态寄存器,0x01000000表示这个寄存器的bit24为1,表示处于Thumb状态,即使用的Thumb指令。
(2)、寄存器PC初始化为任务函数pxCode。
(3)、寄存器LR初始化为函数prvTaskExitError。
(4)、跳过4个寄存器,R12、R3、R2、R1,这四个寄存器不初始化。
(5)、寄存器R0初始化为pvParameters,一般情况下,函数调用会将R0~R3作为输入参数,
R0也可用作返回结果,如果返回值为64位,则R1也会用于返回结果,这里的pvParameters是作为任务函数的参数,保存在寄存器R0中。
(6)、保存EXC_RETURN值,用于退出SVC或PendSV中断的时候处理器应该处于什么态。处理器进入异常或中断服务程序(ISR)时,链接寄存器R14(LR)的数值会被更新为
EXC_RETURN数值,之后该数值会在异常处理结束时触发异常返回。这里人为的设置为OXFFFFFFD,表示退出异常以后CPU进入线程模式并且使用进程栈!
(7)、跳过8个寄存器,R11、R10、R8、R7、R6、R5、R4。
初始化后堆栈结果如下图所示:
任务创建完成以后就会被添加到就绪列表中,FreeRTOS使用不同的列表表示任务的不同状态,在文件tasks.c中就定义了多个列表来完成不同的功能,这些列表如下:
PRIVILEGED_DATA static List_t pxReadyTasksLists[configMAX_PRIORITIES];
PRIVILEGED_DATA static List_t xDelayedTaskListl;
PRIVILEGED_DATA static List_t xDelayedTaskList2;
PRIVILEGED_DATA static List_t* volatile pxDelayedTaskList;
PRIVILEGED_DATA static List_t* volatile pxOverflowDelayedTaskList;
PRIVILEGED_DATA static List_t xPendingReadyList;
列表数组pxReadyTasksLists [] 就是任务就绪列表,数组大小为configMAX_PRIORITIES,也就是说一个优先级一个列表,这样相同优先级的任务就使用一个列表。将一个新创建的任务添加到就绪列表中通过函数prvAddNewTaskToReadyList()来完成,函数如下:
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
taskENTER_CRITICAL();
{
uxCurrentNumberOfTasks++; (1)
if( pxCurrentTCB == NULL )//正在运行任务块为NULL,表示没有任务运行
{
/* There are no other tasks, or all the other tasks are in
the suspended state - make this the current task. */
pxCurrentTCB = pxNewTCB;//将新任务的任务控制块赋值给当前运行任务块
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
{
/* This is the first task to be created so do the preliminary
initialisation required. We will not recover if this call
fails, but we will report the failure. */
prvInitialiseTaskLists(); (2)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
/* If the scheduler is not already running, make this task the
current task if it is the highest priority task to be created
so far. */
if( xSchedulerRunning == pdFALSE )
{//新任务优先级比正在运行优先级高
if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
{
pxCurrentTCB = pxNewTCB; (3)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
uxTaskNumber++;//任务控制块编号 + 1
#if ( configUSE_TRACE_FACILITY == 1 )
{
/* Add a counter into the TCB for tracing only. */
pxNewTCB->uxTCBNumber = uxTaskNumber;
}
#endif /* configUSE_TRACE_FACILITY */
prvAddTaskToReadyList( pxNewTCB ); (4)
portSETUP_TCB( pxNewTCB );
}
taskEXIT_CRITICAL();
if( xSchedulerRunning != pdFALSE )
{
/* If the created task is of a higher priority than the current task
then it should run now. */
//新任务优先级比正在运行优先级高,任务切换
if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
{
taskYIELD_IF_USING_PREEMPTION(); (5)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}