声明:本文为博主的学习篇章,欢迎大家指错,共同学习 在FreeRTOS中最最最主要的部分就是任务,FreeRTOS内部所有的东西基本都是为了任务而存在的。 在FreeRTOS中,一共提供了两种创建任务的形式:动态创建和静态创建。 让我先来看看创建任务的声明
/* 动态创建 */
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, /* 任务函数入口 */
const char * const pcName, /* 任务的名字 */
const configSTACK_DEPTH_TYPE usStackDepth, /* 任务堆栈的深度 */
void * const pvParameters, /* 任务的参数 */
UBaseType_t uxPriority, /* 任务优先级 */
TaskHandle_t * const pxCreatedTask /* 任务TCB句柄 */) PRIVILEGED_FUNCTION;
#endif
/* 静态创建 */
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
BaseType_t xTaskCreateStatic( TaskFunction_t pxTaskCode, /* 任务函数入口 */
const char * const pcName, /* 任务的名字 */
const configSTACK_DEPTH_TYPE usStackDepth, /* 任务堆栈的深度 */
void * const pvParameters, /* 任务的参数 */
UBaseType_t uxPriority, /* 任务优先级 */
TaskHandle_t * const pxCreatedTask /* 任务TCB句柄 */) PRIVILEGED_FUNCTION;
#endif
无论是动态创建还是静态创建都需要将某个置1才能够创建。configSUPPORT_DYNAMIC_ALLOCATION和configSUPPORT_STATIC_ALLOCATION都位于FreeRTOSConfig.h文件中。从声明中可以看出动态创建和静态创建除了函数名不同,其他的参数都是一样的,让我来看看都需要些什么参数!
- pxTaskCode:函数指针,指向任务函数的入口。任务永远不会返回(死循环)。该参数类型TaskFunction_t定义在文件projdefs.h中,定义为:typedef void(*TaskFunction_t)( void * ),即参数为空指针类型并返回空类型。
- pcName:任务名字。字符串的最大长度(包括字符串结束字符)由宏configMAX_TASK_NAME_LEN指定,该宏位于FreeRTOSConfig.h文件中。
- usStackDepth:任务堆栈的深度,能够支持的堆栈变量数量。**注意:这不是字节数。**比如,在16位宽度的堆栈下,usStackDepth定义为100,则实际使用200字节堆栈存储空间。堆栈的宽度乘以深度必须不超过size_t类型所能表示的最大值。比如,size_t为16位,则可以表示堆栈的最大值是65535字节。这是因为堆栈在申请时是以字节为单位的,申请的字节数就是堆栈宽度乘以深度,如果这个乘积超出size_t所表示的范围,就会溢出,分配的堆栈空间也不是我们想要的。
- pvParameters:传入任务的一个参数。
- uxPriority:任务的优先级。在FreeRTOS中任务的优先级越大,其优先级越高。在文件FreeRTOSConfig.h中定义了FreeRTOS可以使用的最高的优先级为configMAX_PRIORITIES,这是一个宏定义,用户可以根据需要自行修改。
- pxCreatedTask:用于保存任务的TCB,也是任务的灵魂。创建任务后可以使用这个句柄引用任务。相当于任务的身份证!
在了解任务创建流程之前需要先了解什么是任务TCB,任务TCB里面都有些什么。 任务TCB也就是任务控制块,它用于存储任务的状态信息。每个任务都有自己的任务TCB。与任务相关的地方都会涉及到任务TCB,先来看看任务TCB都有些什么东西
typedef struct tskTaskControlBlock* TaskHandle_t;
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /* 指向当前任务堆栈的栈顶 */
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings; /* MPU设置 */
#endif
ListItem_t xStateListItem; /* 任务的状态列表项 */
ListItem_t xEventListItem; /* 任务的事件列表项 */
UBaseType_t uxPriority; /* 任务的优先级 */
StackType_t *pxStack; /* 指向当前任务堆栈的起始位置 */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务的名字 */
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
StackType_t *pxEndOfStack; /* 指向当前任务堆栈的栈底 */
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting; /* 保存临界区嵌套深度 */
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTCBNumber; /* 保存一个数值,每个任务都有唯一的值 */
UBaseType_t uxTaskNumber; /* 保存一个特殊数值 */
#endif
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority; /* 保存任务的基础优先级 */
UBaseType_t uxMutexesHeld;
#endif
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
TaskHookFunction_t pxTaskTag;
#endif
#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
#endif
#if( configGENERATE_RUN_TIME_STATS == 1 )
uint32_t ulRunTimeCounter; /* 记录任务在运行状态下执行的总时间 */
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
struct _reent xNewLib_reent;
#endif
#if( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue; /* 与任务通知相关 */
volatile uint8_t ucNotifyState;
#endif
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
uint8_t ucStaticallyAllocated; /* 如果堆栈由静态数组分配,则设置为pdTRUE,如果堆栈是动态分配的,则设置为pdFALSE */
#endif
#if( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
#if( configUSE_POSIX_ERRNO == 1 )
int iTaskErrno;
#endif
} tskTCB;
以上为TCB结构的定义,让我看看都有些什么东西。
- pxTopOfStack:指向当前任务堆栈的栈顶。顾名思义这是一个指向栈顶的指针,也就是说这个指针一直都会指向最后一个入栈的数据。
- xMPUSettings:如果使用MPU,需要这个参数来设置MPU。
- xStateListItem:任务的状态列表项。之前介绍列表项的时候一直有一个参数比较少使用:pvOwner。此时这个参数就会指向这个TCB。这个列表项适用于表示任务的状态信息。在task.c中,定义了一些静态列表变量,其中有就绪、阻塞、挂起列表,例如当某个任务处于就绪态时,调度器就将这个任务TCB的xStateListItem列表项挂接到就绪列表。
- xEventListItem:任务的事件列表项。与上面的参数类似,当队列满的情况下,任务因入队操作而阻塞时,就会将事件列表项挂接到队列的等待入队列表上。
- uxPriority:任务的优先级。
- pxStack:指向当前任务堆栈的起始位置。这个参数与之前的pxTopOfStack很容易弄混淆。pxTopOfStack会随着数据的入栈出栈操作而改变,而pxStack不会变。在文件FreeRTOSConfig.h中定义了宏configTOTAL_HEAP_SIZE,用于表示FreeRTOS的总堆栈的深度。在FreeRTOS中会先先申请一个深度为configTOTAL_HEAP_SIZE的数组,以便分配给任务堆栈,所以任务堆栈申请的空间都是位于总堆栈中的。随着任务的运行,入栈的数据越来越多,堆栈可能会溢出,在堆栈向下增长的系统中,这个变量可用于检查堆栈是否溢出;如果在堆栈向上增长的系统中,要想确定堆栈是否溢出,还需要另外一个变量pxEndOfStack来辅助诊断是否堆栈溢出。
- pcTaskName:用于保存任务的名字。位于文件FreeRTOSConfig.h中的宏configMAX_TASK_NAME_LEN定义了任务名字的最长的长度,包含字符串结束标志。
- pxEndOfStack:指向当前任务堆栈的栈底。如果堆栈向上生长(portSTACK_GROWTH > 0),指针pxEndOfStack指向堆栈尾部,用于检验堆栈是否溢出。与pxStack类似。
- uxCriticalNesting:保存临界区嵌套深度。
- uxTCBNumber:在创建任务时由内核自动分配数值(通常每创建一个任务,值增加1),每个任务的uxTCBNumber值都不同,主要用于调试。
- uxTaskNumber:与变量uxTCBNumber不同,uxTaskNumber的数值不是由内核分配的,而是通过API函数vTaskSetTaskNumber()来设置的,数值由函数参数指定。
- uxBasePriority:如果使用了互斥量(configUSE_MUTEXES == 1),任务优先级被临时提高时,变量uxBasePriority用来保存任务原来的优先级。
- uxMutexesHeld:保存当前任务获取到的互斥量的个数,可能获取多个互斥量
- pxTaskTag:保存任务的标签值(我也没用过,不是很了解)。
- pvThreadLocalStoragePointers:我也不知道啥用。
- ulRunTimeCounter:记录任务在运行状态下执行的总时间。
- xNewLib_reent:我也不知道啥用。
- ulNotifiedValue:与任务通知相关。用于保存通知值。
- ucNotifyState:与任务通知相关。用于保存通知状态。
- ucStaticallyAllocated:API函数xTaskCreate()只能使用动态内存分配的方式创建任务堆栈和任务TCB,如果要使用静态变量实现任务堆栈和任务TCB就需要使用函数xTaskCreateStatic()来实现。如果任务堆栈或任务TCB由静态数组和静态变量实现,则将该变量设置为pdTRUE(任务堆栈空间由静态数组变量实现时为0x01,任务TCB由静态变量实现时为0x02,任务堆栈和任务TCB都由静态变量实现时为0x03),如果堆栈是动态分配的,则将该变量设置为pdFALSE。
- ucDelayAborted和iTaskErrno:不知道用途和意义。
以上就是有关TCB的数据结构,其中有些参数比较偏门,所以我也没深入去了解。接着就是本篇的重头戏创建任务的分析了,为了方便后面的讲解,我这里先动态创建一个任务A,以任务A为例子。
typedef struct tskTaskControlBlock* TaskHandle_t; /* 在文件task.h中 */
TaskHandle_t TaskAHandle = NULL;
xTaskCreate(vTask_A,"Task A",120,NULL,1,&TaskAHandle);
创建任务之前首先我们要明白一些必要的操作: 1、任务函数的入口需要提前声明。 2、任务句柄需要提前定义,因为任务创建的函数会在内部对任务句柄进行数据填充,这里提前一个空的句柄即可。任务句柄被TaskHandle_t修饰,TaskHandle_t在文件task.h中是tskTaskControlBlock*的重定义,也就是说这个任务句柄就是这个任务的TCB。
任务创建函数
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
TCB_t *pxNewTCB; /* 1 */
BaseType_t xReturn;
#if( portSTACK_GROWTH > 0 )
{
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL )
{
pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
if( pxNewTCB->pxStack == NULL )
{
vPortFree( pxNewTCB );
pxNewTCB = NULL;
}
}
}
#else
{
StackType_t *pxStack;
pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /* 2 */
if( pxStack != NULL )
{
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /* 3 */
if( pxNewTCB != NULL )
{
pxNewTCB->pxStack = pxStack; /* 4 */
}
else
{
vPortFree( pxStack );
}
}
else
{
pxNewTCB = NULL;
}
}
#endif
if( pxNewTCB != NULL ) /* 5 */
{
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /* 6 */
{
pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif
/* 7 */
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
prvAddNewTaskToReadyList( pxNewTCB ); /* 8 */
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
#endif
这个API相对来说并不是很复杂,创建任务可以分为八个步骤: 1、创建一些临时变量,防止内存分配出错时对传入参数的误操作。 2、申请任务堆栈的空间,临时保存在pxStack中。空间的大小主要与堆栈的深度和堆栈一个数据的大小有关。 3、如果申请到了任务堆栈的空间,就申请TCB的空间,临时保存在pxNewTCB中,否则就将pxNewTCB指向空。 4、如果申请到了TCB的空间就将申请到的堆栈空间的地址存入TCB的pxStack成员中,否则就释放刚刚申请到的堆栈空间。 5、如果申请到了TCB的空间就需要对TCB里面成员进行初始化,否则返回错误。 6、tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE是一个宏,在FreeRTOS.h中定义。
#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE ( ( ( portUSING_MPU_WRAPPERS == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) || \
( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) )
portUSING_MPU_WRAPPERS、configSUPPORT_DYNAMIC_ALLOCATION、configSUPPORT_STATIC_ALLOCATION是定义在FreeRTOSConfig.h中的宏,是给用户配置的。这些宏主要的作用是定义任务堆栈分配的方式。tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE为1时表示任务堆栈是动态分配的,否则就是静态分配的。 7、初始化任务TCB其他的成员,并将任务TCB赋给参数pxCreatedTask,此后的pxCreatedTask就是任务A的任务TCB了。参考下面部分。 8、将任务A加入到就绪任务列表中,参考下面部分。
初始化任务TCB其他的成员
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 )
{
StackType_t *pxTopOfStack;
UBaseType_t x;
#if( portUSING_MPU_WRAPPERS == 1 ) /* 使用MPU内存保护单元 */
BaseType_t xRunPrivileged;
if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )
{
xRunPrivileged = pdTRUE;
}
else
{
xRunPrivileged = pdFALSE;
}
uxPriority &= ~portPRIVILEGE_BIT;
#endif
#if( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )
{
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
}
#endif
#if( portSTACK_GROWTH < 0 ) /* 堆栈是向下生长的 */
{
pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
#if( configRECORD_STACK_HIGH_ADDRESS == 1 )
{
pxNewTCB->pxEndOfStack = pxTopOfStack;
}
#endif
}
#else /* 堆栈是向上生长的 */
{
pxTopOfStack = pxNewTCB->pxStack;
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
}
#endif
if( pcName != NULL ) /* 将任务的名字存入TCB中 */
{
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
{
pxNewTCB->pcTaskName[ x ] = pcName[ x ];
if( pcName[ x ] == ( char ) 0x00 )
{
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
}
else
{
pxNewTCB->pcTaskName[ 0 ] = 0x00;
}
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) /* 将任务的优先级存入任务TCB中 */
{
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxNewTCB->uxPriority = uxPriority;
#if ( configUSE_MUTEXES == 1 ) /* 如果需要使用互斥量,保存创建任务时的优先级,以便优先级反转后的恢复 */
{
pxNewTCB->uxBasePriority = uxPriority;
pxNewTCB->uxMutexesHeld = 0;
}
#endif
vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); /* 初始化任务状态列表项 */
vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); /* 初始化任务事件列表项 */
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); /* 将任务状态列表项的pvOwner指向任务A */
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /* 初始化事件列表项的xItemValue */
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); /* 将任务事件列表项的pvOwner指向任务A */
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
{
pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
}
#endif
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
{
pxNewTCB->pxTaskTag = NULL;
}
#endif
#if ( configGENERATE_RUN_TIME_STATS == 1 )
{
pxNewTCB->ulRunTimeCounter = 0UL;
}
#endif
#if ( portUSING_MPU_WRAPPERS == 1 )
{
vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth );
}
#else
{
( void ) xRegions;
}
#endif
#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )
{
for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
{
pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL;
}
}
#endif
#if ( configUSE_TASK_NOTIFICATIONS == 1 ) /* 初始化任务TCB中有关任务通知的成员 */
{
pxNewTCB->ulNotifiedValue = 0;
pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
_REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
}
#endif
#if( INCLUDE_xTaskAbortDelay == 1 )
{
pxNewTCB->ucDelayAborted = pdFALSE;
}
#endif
#if( portUSING_MPU_WRAPPERS == 1 )
{
#if( portHAS_STACK_OVERFLOW_CHECKING == 1 )
{
#if( portSTACK_GROWTH < 0 )
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged );
}
#else
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged );
}
#endif
}
#else
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged );
}
#endif
}
#else
{
#if( portHAS_STACK_OVERFLOW_CHECKING == 1 )
{
#if( portSTACK_GROWTH < 0 )
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters );
}
#else
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters );
}
#endif
}
#else
{ /* 初始化任务TCB的任务堆栈 */
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
}
#endif
}
#endif
if( pxCreatedTask != NULL )
{ /* 将任务的任务TCB赋给pxCreatedTask */
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
初始化TCB看着复杂,其实就只有几步比较重要。 首先是MPU部分,FreeRTOS是支持MPU的,第一步是使用MPU时需要用到的配置;第二步是对任务堆栈栈顶的初始化,首先需要知道任务堆栈的生长方向,然后将栈顶地址临时保存到pxTopOfStack中;第三步是将任务的名字存入任务TCB中,如果名字为空就存入0x00;第四步是将任务的优先级存入任务TCB中;第五步是为使用互斥量准备的,保存创建任务时任务的优先级。互斥量的使用会涉及到优先级反转,使用完后需要将优先级恢复为一开始的优先级,此时这个成员变量就能派上用场了;第六步是初始化任务状态列表项和初始化任务事件列表项;第七步是初始化任务TCB中有关任务通知的成员;第八步是初始化任务堆栈并将栈顶地址赋给pxTopOfStack;第九步是将任务的任务TCB赋给pxCreatedTask。
初始化任务堆栈
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
pxTopOfStack--;
*pxTopOfStack = portINITIAL_XPSR; /* xPSR */
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
pxTopOfStack--;
*pxTopOfStack = portINITIAL_EXC_RETURN;
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pxTopOfStack;
}
这里主要是一些寄存器的入栈操作。
将任务加入到就绪任务列表中
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
taskENTER_CRITICAL(); /* 进入临界区 */
{
uxCurrentNumberOfTasks++; /* 任务统计数量加一 */
if( pxCurrentTCB == NULL )
{
pxCurrentTCB = pxNewTCB; /* 如果当前没有任务在运行,就将此任务赋给pxCurrentTCB */
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
{
prvInitialiseTaskLists(); /* 如果当前任务的数量为一,即第一次创建任务就初始化一些列表 */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
if( xSchedulerRunning == pdFALSE ) /* 如果没有打开调度器 */
{
/* 如果新创建的任务优先级大于变量pxCurrentTCB指向的任务优先级,则设置pxCurrentTCB指向当前新创建的任务TCB(确保pxCurrentTCB指向优先级最高的就绪任务) */
if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
{
pxCurrentTCB = pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
uxTaskNumber++; /* 任务控制块的编号加一 */
#if ( configUSE_TRACE_FACILITY == 1 ) /* 可视化追踪功能 */
{
pxNewTCB->uxTCBNumber = uxTaskNumber;
}
#endif
traceTASK_CREATE( pxNewTCB );
prvAddTaskToReadyList( pxNewTCB ); /* 添加任务到就绪列表中 */
portSETUP_TCB( pxNewTCB ); /* 将pxNewTCB设置为空 */
}
taskEXIT_CRITICAL(); /* 退出临界区 */
if( xSchedulerRunning != pdFALSE )
{ /* 如果创建的任务的优先级高于当前任务,那么现在应该运行它 */
if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
{
taskYIELD_IF_USING_PREEMPTION(); /* 进行任务切换 */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
这里面有太多东西需要了解了,并且这里面涉及到了调度器的知识,所以我打算把这个放到下一篇去。 到这里任务的创建就完成了。
|