创建任务
// 静态创建任务的实现
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, // 函数名称,空指针类型地址,标记函数入口
const char * const pcName, // 任务名称,主要用于调试用
const uint32_t ulStackDepth,// 任务堆栈,需先指定数组,单位为4 Byte
void * const pvParameters, // 任务形参
UBaseType_t uxPriority, // 任务优先级
StackType_t * const puxStackBuffer, // 任务栈起始地址,数组的首地址
TCB_t * const pxTaskBuffer )// 任务句柄,其实是任务控制块TCB
{
TCB_t *pxNewTCB; // 定义中间任务控制块
TaskHandle_t xReturn; // 定义最终返回的任务句柄
if ( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ) // 任务控制块与栈都分配空间
{
pxNewTCB = ( TCB_t * ) pxTaskBuffer; // 定义任务控制块 进行读写操作
pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; // 设置控制块的堆栈起始地址
prvInitialiseNewTask( pxTaskCode, // 任务入口
pcName, // 任务名称,字符串形式
ulStackDepth, // 任务栈大小,单位为字
pvParameters, // 任务形参
uxPriority, // 任务优先级
&xReturn, // 任务句柄,任务控制块地址存入 句柄 中
pxNewTCB); // 控制块
prvAddNewTaskToReadyList( pxNewTCB ); // 将任务添加到 就绪列表中
}
else
{
xReturn = NULL; // 如果没有定义控制块,也没有分配栈空间,创建任务失败
}
return xReturn;
}
// 设置任务控制块的栈顶指针,指针8字节对齐,写入函数名称,初始化任务栈,返回任务控制块句柄
static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, // 任务入口
const char * const pcName, // 任务名称,字符串形式
const uint32_t ulStackDepth, // 任务栈大小,单位为字
void * const pvParameters, // 任务形参
uxPriority, // 任务优先级
TaskHandle_t * const pxCreatedTask,// 任务句柄
TCB_t pxNewTCB ) // 任务栈起始地址
{
StackType_t *pxTopOfStack; // 栈顶指针,
UBaseType_t x;
// 栈顶指针 = 栈起始地址 + 栈大小 - 1,入栈自减
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
// 栈顶指针 8 字节对齐,( ~( ( uint32_t ) 0x0007 ) ) ) = 0xFFFF FFF8
pxTopOfStack = ( StackType_t * ) ( ( ( uint32_t ) pxTopOfStack ) & ( ~( ( uint32_t ) 0x0007 ) ) );
// 将任务的名称存储在 TCB 中,长度不得超过 configMAX_TASK_NAME_LEN
for ( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
{
pxNewTCB->pcTaskName[ x ] = pcName[ x ];
if ( pcName[ x ] == 0x00 ) // 任务名结束的条件是 '/0',或长度大于 configMAX_TASK_NAME_LEN
break;
}
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; // 如果超过则直接截断,末尾 = '/0'
// 初始化栈内存,将一些数据压栈
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
if ( ( void * ) pxCreatedTask != NULL ) // 任务句柄 赋值为 任务控制块
{
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB; // 返回任务句柄,本质 任务控制块地址(强转使用)
} // 地址存入 (*pxCreatedTask),pxCreatedTask为句柄地址
}
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
pxTopOfStack--; // 开始压栈,栈顶存放xPSR
*pxTopOfStack = portINITIAL_XPSR; // 最先入栈 0x01000000,也就是xPSR,标识当前ARM、状态或者Thumb状态
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) pxCode; // 入栈函数入口地址,出栈到PC直接执行函数
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) prvTaskExitError; // 压栈LR指针,任务不许返回,返回就跳转执行无限循环
pxTopOfStack -= 5; // R12, R3, R2 and R1 默认初始化为0
*pxTopOfStack = ( StackType_t ) pvParameters; // 压栈R0,R0一般保存函数参数
pxTopOfStack -= 8; // 默认初始化为0,异常发生时自动压栈,R11..R4
return pxTopOfStack; // 最终返回可用栈指针
//传入的栈顶指针是个地址,比如栈顶 0x0200 0000 是栈顶,0x01FF FFF8 存储 函数入口地址
}
就绪列表
就绪列表,是一个列表数组,数组大小由最大任务优先级来指定
在就绪列表数组中,数组下标表示任务的优先级,优先级一致的任务会将列表项插入同一下表的列表
List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
// 创建新任务添加到就绪列表中
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
taskENTER_CRITICAL(); // 进入临界段,确保列表操作不会被中断打断
{
uxCurrentNumberOfTasks++; // 全局任务计数器,标识当前任务数量
if( pxCurrentTCB == NULL ) // 当前任务控制块指针为空,处于初次创建任务;没有任务在运行
{
pxCurrentTCB = pxNewTCB; // 替换,马上执行就是这个任务
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) // 如果是第一次创建任务,任务就绪列表当前为空
{
prvInitialiseTaskLists(); // 初始化相关任务列表,就绪,延时,挂起就绪,阻塞,删除等
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else // 已经创建任务;当前有任务在运行
{
if( xSchedulerRunning == pdFALSE ) // 判断调度器是否开启,是否有任务在运行
{
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 ); // 设置新建任务的任务控制块,强转为 void *类型
}
taskEXIT_CRITICAL();
if( xSchedulerRunning != pdFALSE ) // 如果调度器在运行
{
if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ) // 新建任务优先级 高于 在运行任务优先级
{
taskYIELD_IF_USING_PREEMPTION(); // 执行任务调度
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
static void prvInitialiseTaskLists( void )
{
UBaseType_t uxPriority;
for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
{
vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); // 从优先级0开始,遍历各优先级就绪列表,完成对列表的初始化
} // Index指向END,Value为最大值,next指针指向END
vListInitialise( &xDelayedTaskList1 ); // 初始化延时任务列表,也是阻塞列表
vListInitialise( &xDelayedTaskList2 ); // 阻塞都会指定阻塞时间,所以使用延时列表作为阻塞列表
vListInitialise( &xPendingReadyList ); // 仅用于调度器停止,就绪任务放入该列表,而不是就绪列表
// 执行中断,调度器也被叫停,也会加入到该列表中
#if ( INCLUDE_vTaskDelete == 1 )
{
vListInitialise( &xTasksWaitingTermination ); // 初始化删除未释放内存列表
}
#endif /* INCLUDE_vTaskDelete */
#if ( INCLUDE_vTaskSuspend == 1 )
{
vListInitialise( &xSuspendedTaskList ); // 初始化挂起任务列表
}
#endif /* INCLUDE_vTaskSuspend */
pxDelayedTaskList = &xDelayedTaskList1; // 延时任务列表赋值,通过指针进行操作
pxOverflowDelayedTaskList = &xDelayedTaskList2;
}
#define prvAddTaskToReadyList( pxTCB ) \
traceMOVED_TASK_TO_READY_STATE( pxTCB ); \
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )// 将任务 状态列表项 挂载指定优先级的就绪列表项下,挂载任务到就绪列表
初始化
void vListInitialise( List_t * const pxList )
{
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.xItemValue = portMAX_DELAY;
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
在任务控制块中,有一个 xStateListItem 列表项。
每创建成功一个任务,就会将TCB中的这个列表项挂载在对应(优先级)的列表下
// 将阻塞态任务添加到就绪列表中,区别于上面的任务
// 上面的任务名称是 prvAddNewTaskToReadyList,NewTask
#define prvAddTaskToReadyList( pxTCB ) \
traceMOVED_TASK_TO_READY_STATE( pxTCB ); \
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )