基于 NUCLEO-G431RB 开发板,单核无mmu且不使用 mpu 为例演示最基础的双任务/线程运行,省略大量源码中的配置选项与 if 判断逻辑。
main() 函数逻辑如图:

1. 任务创建
1.1 外部 api 任务创建 task_lCreate
cpp
//11.5 4.1 在堆上动态创建任务(无核亲和性)
BaseType_t task_lCreate(TaskFunc_t pfTaskFunc,
const char* const pcName, // 任务名称
const cfgSTACK_DEPTH_TYPE u32StackDepth, // 栈深度,单位:字(word)
void* const pvParams, // 传递给任务的参数指针
UBaseType_t ulPrio, // 任务优先级
TaskHandle_t* const ppCreatedTask ){ // 返回创建的任务句柄指针(二级指针)
TCB_t* pxNewTCB;
BaseType_t lReturn;
pxNewTCB = task_prv_pxCreateTask( pfTaskFunc, pcName, u32StackDepth, pvParams, ulPrio, ppCreatedTask );
if( pxNewTCB != NULL ){
task_prv_vAddNewTaskToReadyList( pxNewTCB );
lReturn = pdPASS;
}else{
lReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return lReturn;
}
| 步骤 | 代码 | 注释 |
|---|---|---|
| 1 | TCB_t* pxNewTCB; |
声明一个指向任务控制块(TCB)的指针,用于后续存储新任务的上下文信息。 |
| 2 | BaseType_t lReturn; |
声明返回值变量,用于最终返回创建结果(成功/失败)。 |
| 3 | pxNewTCB = task_prv_pxCreateTask( ... ); |
关键 :调用内部私有函数 task_prv_pxCreateTask 来: • 分配 TCB 内存 • 分配任务栈内存 • 初始化 TCB 字段(如任务名、优先级、栈指针等) • 设置初始 CPU 上下文(如寄存器状态) • 若 ppCreatedTask 非 NULL,则将 TCB 地址写入 *ppCreatedTask ✅ 成功时返回 TCB 指针;❌ 失败时返回 NULL |
| 4 | if( pxNewTCB != NULL ) |
判断 TCB 是否成功创建(即内存是否分配成功)。 |
| 5 | task_prv_vAddNewTaskToReadyList( pxNewTCB ); |
关键:将新创建的任务加入就绪列表(ready list),使其可被调度器调度。 |
| 6 | lReturn = pdPASS; |
设置返回值为"成功"。 |
| 9 | return lReturn; |
返回最终结果给调用者。 |
如图:

1.2 内部私有 任务创建 task_prv_pxCreateTask
cpp
// 11.4 3.22
static TCB_t* task_prv_pxCreateTask(TaskFunc_t pxTaskCode, const char* const pcName, const cfgSTACK_DEPTH_TYPE u32StackDepth,
void* const pvParams, UBaseType_t ulPrio, TaskHandle_t* const ppxCreatedTask){
TCB_t* pxNewTCB;
StackType_t* pu32Stack;
pu32Stack = port_pvMallocStack((((size_t)u32StackDepth) * sizeof(StackType_t)));
if( pu32Stack != NULL ) {
pxNewTCB = (TCB_t*) port_pvMalloc(sizeof(TCB_t));
if( pxNewTCB != NULL )
pxNewTCB->pu32BaseOfStack = pu32Stack;
else
port_vFreeStack( pu32Stack );
}else{
pxNewTCB = NULL;
}
if( pxNewTCB != NULL )
task_prv_vInitTCB( pxTaskCode, pcName, u32StackDepth, pvParams, ulPrio, ppxCreatedTask, pxNewTCB, NULL );
return pxNewTCB;
}
| 步骤 | 代码 | 说明 |
|---|---|---|
| 1 | TCB_t* pxNewTCB; |
声明 TCB 指针,用于指向新任务控制块。 |
| 2 | StackType_t* pu32Stack; |
声明栈指针,用于指向为任务分配的运行栈空间。 |
| 3 | pu32Stack = port_pvMallocStack(((( size_t)u32StackDepth) * sizeof( StackType_t))); |
分配任务栈内存 : • 计算所需字节数 = u32StackDepth × sizeof(StackType_t)(通常 StackType_t 是 uint32_t,即 4 字节) • 调用函数 port_pvMallocStack() 分配对齐的栈空间 • 若分配失败,pu32Stack == NULL |
| 4 | pxNewTCB = ( TCB_t * ) port_pvMalloc( sizeof( TCB_t ) ); |
分配 TCB 内存 : 调用通用内存分配函数 port_pvMalloc() 分配一个 TCB 结构体大小的空间 |
| 5 | task_prv_vInitTCB( ... ); |
调用初始化函数 : • 填充 TCB 其他字段(任务名、优先级、参数等) • 初始化 CPU 上下文(设置初始寄存器值,使任务启动时能跳转到 pxTaskCode(pvParams)) • 若 ppxCreatedTask != NULL,则 *ppxCreatedTask = (TaskHandle_t)pxNewTCB |
| 6 | return pxNewTCB; |
返回 TCB 指针(成功时非 NULL,失败时 NULL) |

1.3 内部私有 任务 TCB 初始化 task_prv_vInitTCB
cpp
// 11.2 3.17 初始化新创建的任务控制块
static void task_prv_vInitTCB(TaskFunc_t pxTaskCode, const char* const pcName, const cfgSTACK_DEPTH_TYPE u32StackDepth,
void* const pvParameters, UBaseType_t ulPrio, TaskHandle_t* const ppxCreatedTask,
TCB_t* pxNewTCB, const MemRegion_t* pxRegions){
StackType_t* pu32TopOfStack;
// 1. MPU相关
// 2. 栈初始化填充(便于调试)
// 3. 计算栈顶地址
#if ( portSTACK_GROWTH < 0 )
// 1. 计算未对齐的初始栈顶,pu32BaseOfStack 为系统分配的栈底指针
pu32TopOfStack = &(pxNewTCB->pu32BaseOfStack[u32StackDepth - 1]);
// 2. 对齐到最近的低地址(向下对齐,把地址向下舍入到 8 的倍数)
pu32TopOfStack = (StackType_t*) (((portPOINTER_SIZE_TYPE)pu32TopOfStack) & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK)));
// 3. 断言检查是否对齐
#endif
// 4. 设置任务名称
if( pcName != NULL ){
for(UBaseType_t ulx = ( UBaseType_t ) 0; ulx < ( UBaseType_t ) cfgMAX_TASK_NAME_LEN; ulx++ ){
pxNewTCB->pcName[ ulx ] = pcName[ ulx ];
if( pcName[ ulx ] == ( char ) 0x00 ) break;
}
pxNewTCB->pcName[ cfgMAX_TASK_NAME_LEN - 1U ] = '\0';
}
// 5. 设置任务优先级
if( ulPrio >= ( UBaseType_t ) cfgMAX_PRIO ){
ulPrio = ( UBaseType_t ) cfgMAX_PRIO - ( UBaseType_t ) 1U;
}
pxNewTCB->ulPrio = ulPrio;
// 6. 列表节点初始化
list_vInitNode( &( pxNewTCB->xStateListNode ) );
list_vInitNode( &( pxNewTCB->xEventListNode ) );
listSET_NODE_OWNER( &( pxNewTCB->xStateListNode ), pxNewTCB );
listSET_NODE_VAL( &( pxNewTCB->xEventListNode ), ( TickType_t ) cfgMAX_PRIO - ( TickType_t ) ulPrio );
listSET_NODE_OWNER( &( pxNewTCB->xEventListNode ), pxNewTCB );
// 7. MPU设置
// 8. TLS块初始化
// 9. 栈初始化
// 10. 任务状态和属性初始化
// 11. 任务句柄设置
if( ppxCreatedTask != NULL ){
*ppxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
}

1.4 内部私有 将新任务添加到就绪任务列表中 task_prv_vAddNewTaskToReadyList
cpp
#if ( cfgNUM_CORES == 1 )
static void task_prv_vAddNewTaskToReadyList(TCB_t* pxNewTCB){
taskENTER_CRITICAL();
{
g_ulCurTasksNum = ( UBaseType_t ) ( g_ulCurTasksNum + 1U );
if( g_pxCurTCB == NULL ){
g_pxCurTCB = pxNewTCB;
if( g_ulCurTasksNum == ( UBaseType_t ) 1 ) task_prv_vInitTaskLists();
}else{
if( g_lSchedulerRunning == pdFALSE ){
if( g_pxCurTCB->ulPrio <= pxNewTCB->ulPrio ) g_pxCurTCB = pxNewTCB;
}
}
g_ulTaskNum++;
prvAddTaskToReadyList( pxNewTCB );
portSETUP_TCB( pxNewTCB );
}
taskEXIT_CRITICAL();
if( g_lSchedulerRunning != pdFALSE ) taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxNewTCB );
}
#endif

2. 启动调度器
2.1 外部 api 启动调度器 task_vStartScheduler
cpp
// 11.6 6.1
void task_vStartScheduler(void){
BaseType_t lReturn;
lReturn = task_prv_lCreateIdleTasks();
if( lReturn == pdPASS ) {
portDISABLE_INTERRUPTS();
g_xNextTaskUnblockTime = portMAX_DELAY;
g_lSchedulerRunning = pdTRUE;
g_xTickCount = ( TickType_t ) cfgINITIAL_TICK_COUNT;
( void ) port_lStartScheduler();
}
( void ) g_pxIdleTaskHandles;
( void ) g_ulTopUsedPriority;
}

2.2 内部私有 创建空闲任务 task_prv_lCreateIdleTasks
cpp
// 11.50 3.1
static BaseType_t task_prv_lCreateIdleTasks(void){
BaseType_t lReturn = pdPASS;
BaseType_t lCoreID;
char pcIdleName[ cfgMAX_TASK_NAME_LEN ];
TaskFunc_t pfIdleTaskFunc = NULL;
BaseType_t lIdleTaskNameIdx;
for( lIdleTaskNameIdx = ( BaseType_t ) 0; lIdleTaskNameIdx < ( BaseType_t ) cfgMAX_TASK_NAME_LEN; lIdleTaskNameIdx++ ){
pcIdleName[ lIdleTaskNameIdx ] = cfgIDLE_TASK_NAME[ lIdleTaskNameIdx ];
if( pcIdleName[ lIdleTaskNameIdx ] == ( char ) 0x00 ) break;
}
for( lCoreID = ( BaseType_t ) 0; lCoreID < ( BaseType_t ) cfgNUM_CORES; lCoreID++ ){
pfIdleTaskFunc = prvIdleTask;
lReturn = task_lCreate(pfIdleTaskFunc, pcIdleName, cfgMIN_STACK_SIZE, (void*)NULL, portPRIVILEGE_BIT, &(g_pxIdleTaskHandles[lCoreID]));
if( lReturn == pdFAIL ) break;
}
return lReturn;
}

2.3 内部私有 空闲任务函数 portTASK_FUNCTION
cpp
// 11.10 3.7
static portTASK_FUNCTION( prvIdleTask, pvParameters ){
( void ) pvParameters;
for( ; cfgCONTROL_INFINITE_LOOP(); ) {
if( listCUR_LIST_LEN( &( g_pxReadyTasksLists[ tskIDLE_PRIO ] ) ) > ( UBaseType_t ) cfgNUM_CORES ){
taskYIELD();
}
}
}
多核系统中:
- 在 多核系统 (如 cfgNUM_CORES = N)中,通常会为 每个核心创建一个空闲任务(共 N 个),都运行在 tskIDLE_PRIO 优先级。
- 这些空闲任务都放在同一个就绪链表 g_pxReadyTasksLists[tskIDLE_PRIO] 中。
- 当某个核心运行空闲任务时,如果发现 就绪的空闲任务数量 > 核心数 ,说明有 其他空闲任务也在就绪,当前任务可以主动 taskYIELD(),让调度器尝试调度其他任务(尽管可能性低,但保持公平性)。
单核系统中:
- 只有 1 个空闲任务。
- listCUR_LIST_LEN(...) == 1,不大于
1→ 条件为假 → 不执行taskYIELD()。 - 空闲任务会 持续运行,直到有更高优先级任务就绪。
3. 任务延时
3.1 外部 api 任务延时 task_vDelay
cpp
// 11.11 5.1
void task_vDelay(const TickType_t xTicksToDelay){
BaseType_t lAlreadyYielded = pdFALSE;
if( xTicksToDelay > ( TickType_t ) 0U ){
task_vSuspendAll();
{
task_prv_vAddCurTaskToDelayedList(xTicksToDelay, pdFALSE );
}
lAlreadyYielded = task_lResumeAll();
}
if( lAlreadyYielded == pdFALSE ){
taskYIELD_WITHIN_API();
}
}
让当前运行的任务 **主动挂起指定的系统节拍(ticks)数,**只能在任务上下文中调用
| 步骤 | 代码 | 说明 |
|---|---|---|
| 1 | BaseType_t lAlreadyYielded = pdFALSE; |
初始化标志:记录在恢复调度时是否已触发过上下文切换。 |
| 2 | if( xTicksToDelay > (TickType_t)0U ) |
避免无意义延时 : • 若延时为 0,直接跳过(相当于 taskYIELD()) |
| 3 | task_vSuspendAll(); |
挂起调度器 : • 暂停任务调度(但 不关中断 ) • 允许在临界区内安全修改就绪列表、延时列表等全局结构 • 与 taskENTER_CRITICAL() 不同:中断仍可响应,只是调度被冻结 |
| 4 | task_prv_vAddCurTaskToDelayedList(xTicksToDelay, pdFALSE) |
核心操作 : • 将当前任务(g_pxCurTCB)从就绪列表中移除 • 插入到 延时列表(delayed list) 或 溢出延时列表(overflow delayed list) • 第二个参数 pdFALSE 表示 不是因等待事件而阻塞(纯延时) |
| 5 | lAlreadyYielded = task_lResumeAll(); |
恢复调度器 : • task_lResumeAll() 会: a) 恢复调度 b) 如果此时有更高优先级任务就绪,立即触发一次上下文切换 • 返回值:pdTRUE 表示已发生 yield,pdFALSE 表示未发生 |
| 6 | if( lAlreadyYielded == pdFALSE ) |
检查是否需要手动触发调度 : • 如果 task_lResumeAll() 没有触发切换 (例如:当前仍是最高优先级任务,或无其他任务就绪) • 则必须 显式调用 taskYIELD_WITHIN_API(),因为当前任务已不在就绪列表中(已被移入延时列表),必须让出 CPU! |
| 7 | taskYIELD_WITHIN_API(); |
强制上下文切换 : • 展开为 portYIELD_WITHIN_API() • 触发 PendSV(Cortex-M),切换到下一个就绪任务 |
3.2 内部私有 将当前任务添加到延时列表 task_prv_vAddCurTaskToDelayedList
cpp
// 11.15 3.10
static void task_prv_vAddCurTaskToDelayedList(TickType_t xTicksToWait, const BaseType_t lCanBlockIndefinitely){
TickType_t xTimeToWake;
const TickType_t xConstTickCount = g_xTickCount;
List_t* const pxDelayedList = g_pxDelayedTaskList;
List_t* const pxOverflowDelayedList = g_pxOverflowDelayedTaskList;
if( list_lRmNode( &( g_pxCurTCB->xStateListNode ) ) == ( UBaseType_t ) 0 ){
portRESET_READY_PRIORITY( g_pxCurTCB->ulPrio, g_ulTopReadyPriority );
}
xTimeToWake = xConstTickCount + xTicksToWait;
listSET_NODE_VAL( &( g_pxCurTCB->xStateListNode ), xTimeToWake );
if( xTimeToWake < xConstTickCount ){
list_vInsertSorted( pxOverflowDelayedList, &( g_pxCurTCB->xStateListNode ) );
}else{
list_vInsertSorted( pxDelayedList, &( g_pxCurTCB->xStateListNode ) );
if( xTimeToWake < g_xNextTaskUnblockTime ) g_xNextTaskUnblockTime = xTimeToWake;
}
( void ) lCanBlockIndefinitely;
}
将当前正在运行的任务(g_pxCurTCB)从就绪状态移除,并根据延时时间插入到合适的 延时等待列表 中,使其在指定 tick 后被唤醒。
| 步骤 | 代码 | 说明 |
|---|---|---|
| 1 | TickType_t xTimeToWake; |
声明变量:任务应被唤醒的系统节拍时间(绝对时间)。 |
| 2 | const TickType_t xConstTickCount = g_xTickCount; |
快照当前系统节拍 : • 防止在函数执行期间 g_xTickCount 被 tick 中断修改 • 确保计算一致性 |
| 3 | List_t* const pxDelayedList = g_pxDelayedTaskDList; List_t* const pxOverflowDelayedList = g_pxOverflowDelayedTaskList; |
获取两个全局延时列表指针: • pxDelayedTaskList:存放 未溢出 的延时任务 • pxOverflowDelayedTaskList:存放 tick 计数器溢出后 的延时任务 |
| 4 | if( list_lRmNode( &(g_pxCurTCB->xStateListNode) ) == (UBaseType_t)0 ) |
从当前所在列表中移除任务 : • 通常是从就绪列表中移除 • list_lRmNode 返回列表的节点数量 • 若移除后该优先级无其他就绪任务 → 需更新最高就绪优先级 |
| 5 | portRESET_READY_PRIORITY( g_pxCurTCB->ulPrio, g_ulTopReadyPriority ); |
更新最高就绪优先级 : • 如果当前任务是其优先级队列中 最后一个就绪任务 • 则需降低 g_ulTopReadyPriority(通过位图) • 这是调度器优化的关键:避免每次调度都扫描所有优先级 |
| 6 | xTimeToWake = xConstTickCount + xTicksToWait; |
计算唤醒绝对时间 : • 例如:当前 tick=1000,延时 50 → 唤醒时间=1050 |
| 7 | listSET_NODE_VAL( &(g_pxCurTCB->xStateListNode), xTimeToWake ); |
将唤醒时间存入链表节点的"值"字段 : • 后续 list_vInsertSorted 会按此值排序 • 确保延时列表按唤醒时间 升序排列(最早唤醒在前) |
| 8 | if( xTimeToWake < xConstTickCount ) |
判断是否发生 tick 计数器溢出 : • 当 xConstTickCount + xTicksToWait 超过 UINT32_MAX 时,结果会 回绕(wrap around) ,变成一个小于 xConstTickCount 的值 • 例如:xConstTickCount = 0xFFFFFF00, xTicksToWait = 0x200 → xTimeToWake = 0x100(溢出) |
| 9 | list_vInsertSorted( pxOverflowDelayedList, ... ); |
插入溢出延时列表 : • 用于处理跨越 tick 溢出边界的情况 • tick 中断处理时会先处理普通列表,等其变空后再交换两个列表(见下文) |
| 10 | else { list_vInsertSorted( pxDelayedList, ... ); } |
插入普通延时列表 : • 按 xTimeToWake 升序插入 |
| 11 | if( xTimeToWake < g_xNextTaskUnblockTime ) g_xNextTaskUnblockTime = xTimeToWake; |
更新全局最早唤醒时间 : • g_xNextTaskUnblockTime 用于 tick 中断快速判断是否需要处理延时任务 • 若新任务唤醒时间更早,则更新它 • 可避免每次 tick 都遍历整个延时列表 |
| 12 | (void) lCanBlockIndefinitely; |
抑制编译器"未使用参数"警告 : • 该参数在某些配置下可能被使用(如支持"无限阻塞"时优化内存) • 但在此简化实现中未启用,故静默忽略 |

3.3 内部使用 挂起调度器 task_vSuspendAll
cpp
// 11.13 6.3
void task_vSuspendAll(void){
portSOFTWARE_BARRIER();
g_ulSchedulerSuspended = ( UBaseType_t ) ( g_ulSchedulerSuspended + 1U );
portMEMORY_BARRIER();
}
临时禁止任务切换,但允许中断继续执行 ;增加调度器挂起计数器(只有当计数器减回 0 时,调度器才真正恢复)。
3.4 内部使用 恢复调度器task_vSuspendAll
cpp
// 11.14 6.4
BaseType_t task_lResumeAll(void){
TCB_t * pxTCB = NULL;
BaseType_t lAlreadyYielded = pdFALSE;
{
taskENTER_CRITICAL();
{
BaseType_t lCoreID;
lCoreID = ( BaseType_t ) portGET_CORE_ID();
cfgASSERT( g_ulSchedulerSuspended != 0U );
g_ulSchedulerSuspended = ( UBaseType_t ) ( g_ulSchedulerSuspended - 1U );
if( g_ulSchedulerSuspended == ( UBaseType_t ) 0U ){
if( g_ulCurTasksNum > ( UBaseType_t ) 0U ){
while( listLIST_IS_EMPTY( &g_xPendingReadyList ) == pdFALSE ){
pxTCB = listGET_OWNER_OF_HEAD( ( &g_xPendingReadyList ) );
listRM_NODE( &( pxTCB->xEventListNode ) );
portMEMORY_BARRIER();
listRM_NODE( &( pxTCB->xStateListNode ) );
prvAddTaskToReadyList( pxTCB );
if( pxTCB->ulPrio > g_pxCurTCB->ulPrio ) g_lYieldPendings[ lCoreID ] = pdTRUE;
}
if( pxTCB != NULL ) task_prv_vResetNxtTaskUnblockTime();
{
TickType_t xPendedCounts = g_xPendedTicks;
if( xPendedCounts > ( TickType_t ) 0U ){
do{
if( task_lIncrementTick() != pdFALSE ) g_lYieldPendings[ lCoreID ] = pdTRUE;
--xPendedCounts;
} while( xPendedCounts > ( TickType_t ) 0U );
g_xPendedTicks = 0;
}
}
if( g_lYieldPendings[ lCoreID ] != pdFALSE ){
lAlreadyYielded = pdTRUE;
taskYIELD_TASK_CORE_IF_USING_PREEMPTION( g_pxCurTCB );
}
}
}
}
taskEXIT_CRITICAL();
}
return lAlreadyYielded;
}
恢复调度器 ,处理在此期间积累的待处理任务和节拍。

3.5 内部私有 重置下一个任务解除阻塞的时间 task_prv_vResetNxtTaskUnblockTime
cpp
// 11.8 3.15
static void task_prv_vResetNxtTaskUnblockTime(void){
if( listLIST_IS_EMPTY( g_pxDelayedTaskList ) != pdFALSE ){
g_xNextTaskUnblockTime = portMAX_DELAY;
}else{
g_xNextTaskUnblockTime = listGET_HEAD_VAL( g_pxDelayedTaskList );
}
}
- 检查 主延时任务列表 (
g_pxDelayedTaskList)是否为空:- 如果为空 :说明当前没有任务在等待超时唤醒,于是将
g_xNextTaskUnblockTime设为最大值(portMAX_DELAY),表示"无需关注延时唤醒"。 - 如果不为空 :取列表头部任务的唤醒时间(因为列表按唤醒时间升序排列),作为下一个需要处理的唤醒点。
- 如果为空 :说明当前没有任务在等待超时唤醒,于是将
3.6 内部使用 SysTick 中断服务程序调用task_lIncrementTick
cpp
// 11.9 8.2
BaseType_t task_lIncrementTick(void){
TCB_t* pxTCB;
TickType_t xItemValue;
BaseType_t lSwitchRequired = pdFALSE;
if( g_ulSchedulerSuspended == ( UBaseType_t ) 0U ){
const TickType_t xConstTickCount = g_xTickCount + ( TickType_t ) 1;
g_xTickCount = xConstTickCount;
if( xConstTickCount == ( TickType_t ) 0U ) taskSWITCH_DELAYED_LISTS();
if( xConstTickCount >= g_xNextTaskUnblockTime ) {
for( ; ; ){
if( listLIST_IS_EMPTY( g_pxDelayedTaskList ) != pdFALSE ){
g_xNextTaskUnblockTime = portMAX_DELAY;
break;
} else{
pxTCB = listGET_OWNER_OF_HEAD( g_pxDelayedTaskList );
xItemValue = listGET_NODE_VAL( &( pxTCB->xStateListNode ) );
if( xConstTickCount < xItemValue ){
g_xNextTaskUnblockTime = xItemValue;
break;
}
listRM_NODE( &( pxTCB->xStateListNode ) );
if(listGET_NODE_CONTAINER( &( pxTCB->xEventListNode ) ) != NULL) listRM_NODE(&( pxTCB->xEventListNode));
prvAddTaskToReadyList( pxTCB );
if( pxTCB->ulPrio > g_pxCurTCB->ulPrio ) lSwitchRequired = pdTRUE;
}
}
}
if( listCUR_LIST_LEN( &( g_pxReadyTasksLists[ g_pxCurTCB->ulPrio ] ) ) > 1U ) lSwitchRequired = pdTRUE;
if( g_lYieldPendings[ 0 ] != pdFALSE ) lSwitchRequired = pdTRUE;
}else{
g_xPendedTicks += 1U;
}
return lSwitchRequired;
}
- 更新系统节拍计数器
- 将全局 tick 计数
g_xTickCount增加 1。
- 将全局 tick 计数
- 处理 tick 计数器溢出
- 当
g_xTickCount回绕到 0 时,交换主延时列表与溢出延时列表(taskSWITCH_DELAYED_LISTS()),以正确处理跨溢出边界的延时任务。
- 当
- 循环唤醒所有到期任务
- 检查当前 tick 是否 ≥
g_xNextTaskUnblockTime(最早唤醒时间)。 - 若是,则循环遍历延时列表头部 :
- 只要头部任务的唤醒时间 ≤ 当前 tick,就将其从延时列表中移除;
- 同时从事件等待列表(如存在)中移除;
- 将该任务加入就绪列表;
- 若其优先级高于当前运行任务,则标记需要上下文切换(
lSwitchRequired = pdTRUE);
- 一旦遇到未到期任务(唤醒时间 > 当前 tick),即停止循环(因列表按唤醒时间升序排列)。
- 检查当前 tick 是否 ≥
- 更新下一个唤醒时间
- 若延时列表变空,设
g_xNextTaskUnblockTime = portMAX_DELAY; - 否则,将其更新为新头部任务的唤醒时间。
- 若延时列表变空,设
- 支持同优先级时间片轮转(Round-Robin)
- 如果当前优先级的就绪任务数量 > 1,标记需要切换,实现同优先级任务间的公平调度。
- 响应外部 yield 请求
- 如果
g_lYieldPendings[0]被置位(如 API 调用中请求切换),则标记需要切换。
- 如果
- 处理调度器挂起状态
- 若调度器被挂起(
g_ulSchedulerSuspended > 0),不处理任何任务唤醒 ,仅将g_xPendedTicks加 1,留待调度器恢复时批量处理。
- 若调度器被挂起(
- 返回切换需求标志
- 返回
pdTRUE表示有更高优先级任务就绪或需轮转,应触发上下文切换; - 返回
pdFALSE表示无需切换,继续执行当前任务。
- 返回
4. 源码
4.1 tasks.h
cpp
// tasks.h
#ifndef TASKS_H
#define TASKS_H
#include "port.h"
#include "list.h"
#include "RTOS.h"
#include <stdint.h>
#include <stddef.h>
// 禁用 C++ 名称修饰
#ifdef __cplusplus
extern "C" {
#endif
// ========================
// 一. 配置宏定义
// ========================
// ========================
// 二. 类型定义
// ========================
// 2.1 任务控制块
struct xTCB;
typedef struct xTCB* TaskHandle_t;
// 2.2 任务标签函数类型
// 2.3 任务状态
// 2.4 任务通知动作
// 2.5 超时结构
// 2.6 内存保护
typedef struct xMEMORY_REGION MemRegion_t;
// 2.7 内存保护上的任务参数结构体
// 2.8 任务状态结构体,与原版相同,共 12 个字段
// 高地址 ┌───────────────┐
// │ │
// │ 未使用区域 │
// │ │
// ├───────────────┤ ← pu32TopOfStack (当前栈顶,动态变化,随着压栈 Top 向下移动),pu32EndOfStack为初始时Top(不变)
// │ │ 使用
// │ 已使用区域 │ 深度
// │ │ ← pu32TopOfStack 使用一段后
// │ │
// 低地址 └───────────────┘ ← pu32BaseOfStack (栈的起始/最低地址,创建任务时系统分配)
// 2.9 eSleepModeStatus 低功耗任务睡眠模式状态
// 2.10 空闲任务优先级
#define tskIDLE_PRIO ((UBaseType_t)0U)
// 2.11 无核亲和性掩码(全1)
// 2.12 任务函数类型(自定义)
typedef void (*TaskHook_t)(void*);
// ========================
// 三. 其他定义
// ========================
// 3.1 任务让出cpu,移植层实现
#define taskYIELD() portYIELD()
// 3.2 临界区相关,移植层实现
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
// 3.3 中断相关,移植层实现
// 3.4 调度器状态
// 3.5 核心ID是否有效
// ========================
// 四. 任务创建 API
// ========================
// 4.1 在堆上动态创建任务(无核亲和性)
BaseType_t task_lCreate(TaskFunc_t pfTaskFunc,
const char* const pcName, // 任务名称
const cfgSTACK_DEPTH_TYPE u32StackDepth, // 栈深度,单位:字(word)
void* const pvParams, // 传递给任务的参数指针
UBaseType_t ulPrio, // 任务优先级
TaskHandle_t* const ppCreatedTask ); // 返回创建的任务句柄指针(二级指针)
// 4.2 在堆上动态创建任务(有核亲和性)
// 4.3- 4.9 静态和MPU创建任务版
// 4.10 任务删除
void task_vDelete(TaskHandle_t pxTask);
// ========================
// 五. 任务控制 API
// ========================
// 5.1 让当前任务延迟(阻塞)指定的时钟节拍(ticks)数
void task_vDelay(const TickType_t xTicksToDelay);
// 5.2 让当前任务延迟(阻塞)直到系统节拍计数器(xTickCount)达到*pu32PreviousWakeTime + u32TimeIncrement 这个绝对时间点
// 5.3 强制解除一个处于阻塞(delayed 或 blocked)状态的任务的延迟,使其立即进入就绪状态(ready),可以被调度运行
// 5.4 获取指定任务的当前优先级值
// 5.5 中断版本获取指定任务的当前优先级值
// 5.6 获取指定任务的基础优先级值
// 5.7 中断版本获取指定任务的基础优先级值
// 5.8 获取指定任务的当前状态
// 5.9 获取指定任务的详细运行信息,并填充到用户提供的 TaskStatus_t 结构体中
// 5.10 动态修改任务优先级
// 5.11 将指定任务置于 eSuspended 状态,使其不再被调度器选中运行
// 5.12 将指定任务从 eSuspended 状态唤醒,使其重新进入就绪状态(ready),等待调度运行
// 5.13 5.12 中断版
// 5.14 设置任务的 CPU 核心亲和性
// 5.15 获取任务的 CPU 核心亲和性掩码
// 5.16 禁用目标任务的被抢占能力,不会被更高优先级的任务打断
// 5.17 启用目标任务的被抢占能力
// ========================
// 六. 调度器控制 API
// ========================
// 6.1 初始化调度器并开始多任务调度
void task_vStartScheduler(void);
// 6.2 关闭调度器,停止多任务调度
// 6.3 挂起整个调度器(即禁止任务切换),但不关闭中断
void task_vSuspendAll(void);
// 6.4 恢复整个调度器(即允许任务切换)
BaseType_t task_lResumeAll(void);
// ========================
// 七. 任务相关工具 API
// ========================
// 7.1 获取自调度器启动以来经过的系统节拍(tick)数
// 7.2 中断版本获取自调度器启动以来经过的系统节拍(tick)数
// 7.3 获取当前系统中已创建的任务(包括运行、就绪、阻塞、挂起等所有状态)的总数
// 7.4 获取指定任务的名称字符串指针
// 7.5 通过任务名称查找对应任务句柄
// 7.6 获取静态创建任务所使用的内部缓冲区指针
// 7.7 返回指定任务自创建以来栈内存剩余空间的最小值(高水位线),单位是 "字(words)"
// 7.8 返回值自定义版的 7.7
// 7.9 设置指定任务的应用层钩子函数(即任务标签)
// 7.10 获取指定任务的应用层钩子函数
// 7.11 中断版本获取指定任务的应用层钩子函数
// 7.12 设置指定任务的线程本地存储指针
// 7.13 获取指定任务的线程本地存储指针
// 7.14 检测到某个任务的堆栈溢出时,内核会立即调用此函数
// 7.15 由系统内置的 空闲任务(Idle Task) 周期性调用
// 7.16 每次系统节拍中断(SysTick ISR)处理完内核调度工作之后每次 tick 中断发生时被自动调用
// 7.17 静态分配空闲任务(Idle Task)内存
// 7.18 多核下为被动空闲任务静态分配内存的钩子函数
// 7.19 显式调用某个任务所关联的应用层钩子函数
// 7.20 单核下获取空闲任务(Idle Task)句柄
// 7.21 通用:返回该核心对应的空闲任务句柄
// 7.22 一次性获取系统中所有任务的运行状态快照(如任务名、优先级、状态、运行时间等),返回一个包含每个任务当前状态信息的结构体数组
// 成功返回实际填充的 TaskHandle_t 结构体数量,失败(数组太小)返回 0。
// 7.23 将系统中所有任务的简要状态信息(如任务名、状态、优先级、堆栈剩余等)
// 格式化为人类可读的 ASCII 表格字符串,并写入用户提供的缓冲区 pcWriteBuffer
// 7.24 7.23的旧版本
// 7.25 将每个任务自系统启动以来的 CPU 使用情况(运行时间 + 占比)格式化为人类可读的 ASCII 表格,并写入用户提供的缓冲区
// 7.26 7.25的旧版本
// 7.27 返回指定任务自创建以来累计消耗的运行时间单位数
// 7.28 返回指定任务自系统启动以来占用的 CPU 时间百分比
// 7.29 空闲任务版本的 7.27
// 7.30 空闲任务版本的 7.28
// 7.31 任务通知基础函数
// 7.32 7.31 的旧版宏封装
// 7.33 7.31 的新版宏封装,向任务的指定通知索引发送通知
// 7.34 7.32版发送通知的同时,还能返回目标任务在修改前的原始通知值
// 7.35 7.33版发送通知的同时,还能返回目标任务在修改前的原始通知值
// 7.36 - 7.40 31-35的中断版
// 7.41 让当前任务进入阻塞状态,直到收到指定索引的通知;收到后可读取值、清除位,并继续执行
// 7.42 7.41 的旧版宏封装
// 7.43 7.41 的索引版宏封装
// 7.44 旧版本通知版的 7.31,用于给任务的默认通知索引发送通知(增加 1)
// 7.45 新版 7.44,用于给任务的默认通知索引发送通知(增加 1)
// 7.46 - 7.48 中断版的 7.44 和 7.45
// 7.49 底层 让当前任务阻塞等待某个通知值 > 0;一旦收到,就将该值减 1,并返回减之前的值
// 7.50 旧版本宏封装的7.49,默认索引
// 7.51 新版本宏封装的7.49
// 7.52 任务通知中用于清除通知状态
// 7.53 旧版本宏封装的7.52,默认索引
// 7.54 新版本宏封装的7.52
// 7.55 清除通知值中的特定位
// 7.56 7.55 的旧版宏封装,默认索引
// 7.57 7.55 的索引版宏封装
// 7.58 记录当前系统时间(包括 tick 计数和溢出次数)到一个 TimeOut_t 结构体中
// 7.59 检查是否发生超时
// 7.60 人为地将系统 tick 向前推进指定的数量
// 7.61 在重启调度器之前,必须由应用程序调用此函数,以重置任务的内部状态
// ========================
// 八. 调度器内部实现
// ========================
// 8.1 移植层,任务切换
#if ( cfgNUM_CORES == 1 )
#define taskYIELD_WITHIN_API() portYIELD_WITHIN_API()
#else
#endif
// 8.2 每次系统 tick 中断(SysTick)发生时被调用,用于:
BaseType_t task_lIncrementTick(void);
// 8.3 将当前正在运行的任务放入一个"事件等待列表"中,并设置超时时间,使其进入阻塞状态,直到被唤醒或超时
// 8.4 将当前任务无序地添加到事件列表 pxEventList 中,同时将一个值 xItemValue 存入任务 TCB 中
// 8.5 在特定限制条件下将当前任务放入事件等待列表(如信号量、队列的阻塞等待队列),使其进入阻塞状态
// 8.6 当某个事件发生(如信号量被释放、队列收到数据)或超时到期时,将等待该事件的任务从阻塞状态移回就绪状态
// 8.7
// 8.8 上下文切换
#if ( cfgNUM_CORES == 1 )
portDONT_DISCARD void task_vSwitchContext( void );
#endif
// 8.9 重置当前任务的事件列表项中存储的值,并返回其旧值
// 8.10 获取当前正在运行的任务的句柄
// 8.11 获取指定 CPU 核心上当前正在运行的任务句柄
// 8.12 调度器本应触发任务切换(yield),但由于调度器被挂起(scheduler suspended)而未能执行,现在需要补上这次切换请求
// 8.13 查询当前调度器(Scheduler)的运行状态
// 8.14 实现优先级继承机制
// 8.15 在互斥锁释放时恢复持有者任务的原始优先级,结束优先级继承
// 8.16 当任务因等待互斥锁超时而放弃等待时,若该任务曾触发优先级继承,则据此回调恢复持有者优先级
// 8.17 获取指定任务的用户自定义任务编号
// 8.18 为指定任务设置一个用户自定义的任务编号
// 8.19 在 tickless idle 模式下,将系统 tick 计数器向前推进指定的 tick 数,以补偿低功耗睡眠期间"丢失"的 ticks
// 8.20 在进入 tickless 睡眠前,由内核调用以确认当前是否允许进入低功耗模式
// 8.21 递增当前任务持有的互斥锁计数(用于递归互斥锁),并返回当前任务句柄
// 8.22 内部版本的超时状态初始化函数,直接设置 TimeOut_t 结构体中的 tick 值和溢出计数
// 8.23 在多核(SMP)系统中,于 API 内部安全地触发任务切换
// 8.24 - 8.27 临界区相关
// 8.28 - 8.32 MPU相关
#endif
#ifdef __cplusplus
}
#endif
4.2 tasks.c
cpp
// tasks.c
#include "tasks.h"
#include "stm32g431xx.h"
// ========================
// 一. 宏定义
// ========================
// 1.1 抢占式/协作式调度下的任务切换
#if ( cfgNUM_CORES == 1 )
#define taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxTCB ) \
do { \
( void ) ( pxTCB ); \
portYIELD_WITHIN_API(); \
} while( 0 )
#define taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB ) \
do { \
if( g_pxCurTCB->ulPrio < ( pxTCB )->ulPrio ) \
portYIELD_WITHIN_API(); \
} while( 0 )
#else
#endif
// 1.2 任务通知状态
// 1.3 任务栈填充字节,用于"高水位标记"
#define taskSTACK_FILL_BYTE ( 0xa5U )
// 1.4 资源分配方式标志(静态/动态分配栈和 TCB)
// 1.5 是否在创建任务时用已知值填充栈内存的宏定义标志,默认填充
// 1.6 以字符形式表示任务状态的常量,主要用于调试
// 1.7 禁用static便于单元测试,未启用
// 1.8 空闲任务名称为 "IDLE"
#define cfgIDLE_TASK_NAME "IDLE"
// 1.9 查找最高优先级的就绪任务
#if ( cfgUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )
#else
#define taskRECORD_READY_PRIORITY( ulPrio ) portRECORD_READY_PRIORITY( ( ulPrio ), g_ulTopReadyPriority )
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
do { \
UBaseType_t ulTopPrio; \
portGET_HIGHEST_PRIORITY( ulTopPrio, g_ulTopReadyPriority ); \
listGET_OWNER_OF_NEXT_NODE( g_pxCurTCB, &( g_pxReadyTasksLists[ ulTopPrio ] ) ); \
} while( 0 )
#define taskRESET_READY_PRIORITY( ulPrio ) \
do { \
if( listCUR_LIST_LEN( &( g_pxReadyTasksLists[ ( ulPrio ) ] ) ) == ( UBaseType_t ) 0 ) \
{ \
portRESET_READY_PRIORITY( ( ulPrio ), ( g_ulTopReadyPriority ) ); \
} \
} while( 0 )
#endif
// 1.10 处理延时任务列表溢出
#define taskSWITCH_DELAYED_LISTS() \
do { \
List_t * pxTemp; \
pxTemp = g_pxDelayedTaskList; \
g_pxDelayedTaskList = g_pxOverflowDelayedTaskList; \
g_pxOverflowDelayedTaskList = pxTemp; \
g_lNumOfOverflows = ( BaseType_t ) ( g_lNumOfOverflows + 1 ); \
task_prv_vResetNxtTaskUnblockTime(); \
} while( 0 )
// 1.11 将一个任务添加到就绪任务列表中,使其可以被调度器选中运行
#define prvAddTaskToReadyList( pxTCB ) \
do { \
taskRECORD_READY_PRIORITY( ( pxTCB )->ulPrio ); \
listINSERT_END( &( g_pxReadyTasksLists[ ( pxTCB )->ulPrio ] ), &( ( pxTCB )->xStateListNode ) ); \
} while( 0 )
// 1.12 从任务句柄(Task Handle)安全地获取对应的 TCB(Task Control Block)指针
// 1.13 在 FreeRTOS 的事件链表(event list)机制中,标记某个任务的值正在被借用作其他用途,此时禁止因任务优先级变更而修改该值
// 1.14 该任务当前没有在任何 CPU 核心上运行
// 1.15 该任务当前正在某个核心上运行,但已经决定要主动让出 CPU(yield),即将被切换出去
// 1.16 判断任务是否在运行
// 1.17 标识某个任务是一个空闲任务
// 1.18 临界区嵌套计数
// 1.19 1字节等于8位
// 1.20 在多核系统中请求指定核心(core)进行上下文切换(yield)
// ========================
// 二. 类型和全局变量定义
// ========================
// 2.1 任务控制块 (TCB)
typedef struct TCB {
// 1 栈顶,任务被暂停时 CPU 寄存器里的内容都存到栈里。这个指针指向栈顶,下次从这里继续读
volatile StackType_t* pu32TopOfStack;
// 2. MPU相关
// 3. 任务核亲和性掩码
// 3. 表示在 ready/blocked/suspended 中某个列表
Node_t xStateListNode;
// 5. 用于在某个事件列表中等待信号
Node_t xEventListNode;
// 6. 优先级
UBaseType_t ulPrio;
// 7. 栈底指针
StackType_t* pu32BaseOfStack;
// 多核系统时,每个任务有一个运行状态和属性
#if (cfgNUM_CORES > 1)
// 8. 记录在哪个核上运行
// 9. 任务属性,例如是否是 idle 任务
#endif
// 10. 任务名
char pcName[cfgMAX_TASK_NAME_LEN];
// 11. 是否禁用抢占
// 12. 栈的最高地址记录,用于栈溢出检测
StackType_t* pu32EndOfStack;
// 13. 临界区嵌套深度
// 13. TCB 的唯一编号,内核自增
// 15. 任务的唯一编号,用户指定
UBaseType_t g_ulTaskNum;
// 16. 基础优先级
UBaseType_t ulBasePrio;
// 17. 持有的互斥量数量
// 18. 任务标签函数指针(每个任务关联的自定义函数)
// 19. TLS,用户调用的,任务私有上下文管理
// 20. 任务累计运行时间计数器
// 21. TLS 块,用于 C 库存储任务的 TLS 数据
// 22. 任务通知
// 23. 任务通知状态
// 23. 静态分配标记,用于区分任务是静态还是动态创建,防止错误释放内存
// 25. 延时是否被中断取消
// 26. 每个任务独立的 errno,用于 POSIX 兼容,缺时后启用此功能,errno 会全局共享
} TCB_t;
// 2.2 当前运行的任务指针
#if cfgNUM_CORES == 1
TCB_t* volatile g_pxCurTCB = NULL;
#endif
// 2.3 就绪任务列表数组:每个优先级一个双向链表
static List_t g_pxReadyTasksLists[cfgMAX_PRIO];
// 2.4 延时任务列表1:用于存储未到期的延时任务
static List_t g_xDelayedTaskList1;
// 2.5 延时任务列表2:用于存储已到期的延时任务(溢出的延时任务)
static List_t g_xDelayedTaskList2;
// 2.6 指向当前正在使用的延时任务列表的指针
static List_t* volatile g_pxDelayedTaskList;
// 2.7 指向当前正在使用的溢出延时任务列表的指针
static List_t* volatile g_pxOverflowDelayedTaskList;
// 2.8 待处理就绪任务列表:用于存储在延时任务处理完成后,需要立即加入就绪队列的任务
static List_t g_xPendingReadyList;
// 2.9 存放已被删除(deleted)但尚未释放内存的任务控制块(TCB)和栈
static List_t g_xTasksWaitingTermination;
// 2.10 已删除任务等待清理计数:记录当前有多少个任务已被删除但尚未被清理
// 2.11 挂起任务列表:用于存储被挂起的任务
static List_t g_xSuspendedTaskList;
// 2.12 全局的 errno 变量,在任务切换时自动更新它
// 2.13 记录当前系统中已创建的任务总数(包括就绪、阻塞、挂起等,但不包括已删除未清理的任务)
static volatile UBaseType_t g_ulCurTasksNum = (UBaseType_t) 0U;
// 2.14 系统节拍计数器,由 SysTick 中断递增,表示自调度器启动以来经过的 tick 数
static volatile TickType_t g_xTickCount = (TickType_t) 0U;
// 2.15 当前所有就绪任务中的最高优先级值,用于加速调度器查找
static volatile UBaseType_t g_ulTopReadyPriority = tskIDLE_PRIO;
// 2.16 标志位,表示调度器是否已启动(pdTRUE)或尚未启动(pdFALSE)
static volatile BaseType_t g_lSchedulerRunning = pdFALSE;
// 2.17 在调度器挂起期间错过的 tick 数量,恢复调度时需补处理
static volatile TickType_t g_xPendedTicks = ( TickType_t ) 0U;
// 2.18 每个核心一个标志,表示该核心在临界区中被请求 yield,需在退出临界区后执行上下文切换
static volatile BaseType_t g_lYieldPendings[cfgNUM_CORES] = { pdFALSE };
// 2.19 记录 xTickCount 回绕(从最大值变为 0)的次数,用于构建更长周期的时间基准
static volatile BaseType_t g_lNumOfOverflows = ( BaseType_t ) 0;
// 2.20 任务创建序号计数器,用于为每个新任务分配唯一 ID(非 volatile,因只在任务创建时修改,且在临界区内)
static UBaseType_t g_ulTaskNum = ( UBaseType_t ) 0U;
// 2.21 下一个即将解除阻塞(unblock)的任务的唤醒时间(tick 值),用于优化 tick 处理效率
static volatile TickType_t g_xNextTaskUnblockTime = ( TickType_t ) 0U;
// 2.22 存储每个核心对应的空闲任务(Idle Task)的句柄,用于内核管理和调试
static TaskHandle_t g_pxIdleTaskHandles[cfgNUM_CORES];
// 2.23 常量全局符号,告诉 OpenOCD 使用了从优先级 0 到 cfgMAX_PRIO - 1 的所有优先级列表
static const volatile UBaseType_t g_ulTopUsedPriority = cfgMAX_PRIO - 1;
// 2.24 计数器,记录当前调度器被 vTaskSuspendAll() 挂起的嵌套层数;当值为 0 时表示调度器正常运行,非 0 时表示调度器被挂起,任务切换被禁止
static volatile UBaseType_t g_ulSchedulerSuspended = (UBaseType_t) 0U;
// 2.25 记录每个核心上"当前正在运行的任务"被切换进来(switched in)时的计数器值
// 2.26 累计每个核心自调度器启动以来的总运行时间(所有任务执行时间之和)
// ========================
// 三. 私有函数声明
// ========================
// 3.1 创建空闲任务
static BaseType_t task_prv_lCreateIdleTasks(void);
// 3.5 检查某个任务是否处于"挂起(Suspended)"状态
// 3.6 初始化函数,用于创建并清空所有任务状态链表(就绪、延时、挂起等),在第一个任务被创建时自动调用一次
static void task_prv_vInitTaskLists(void);
// 3.7 主空闲任务函数,运行在启动调度器的核心上,负责回收已删除任务的内存、执行低功耗钩子等,系统必需的后台任务
static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters );
// 3.8 释放任务控制块及其关联的栈所占用的内存
// 3.9 检查并清理等待终止的任务
// 3.10 将当前任务添加到延时任务列表中
static void task_prv_vAddCurTaskToDelayedList(TickType_t xTicksToWait, const BaseType_t lCanBlockIndefinitely);
// 3.11 在单个任务列表中填充任务状态信息
// 3.12 在单个任务列表中搜索指定名称的任务
// 3.13 检查任务栈的可用空间(高水位标记)
// 3.14 获取预计的空闲时间(用于低功耗模式)
// 3.15 重置下一个任务解除阻塞的时间
static void task_prv_vResetNxtTaskUnblockTime(void);
// 3.16 将任务名称写入缓冲区(用于格式化统计信息)
// 3.17 初始化新创建的任务控制块
static void task_prv_vInitTCB(TaskFunc_t pxTaskCode, const char* const pcName, const cfgSTACK_DEPTH_TYPE u32StackDepth,
void* const pvParameters, UBaseType_t ulPrio, TaskHandle_t* const ppxCreatedTask,
TCB_t* pxNewTCB, const MemRegion_t* pxRegions);
// 3.18 将新任务添加到就绪任务列表中
static void task_prv_vAddNewTaskToReadyList(TCB_t* pxNewTCB);
// 3.19 -3.21 静态任务和MPU相关
// 3.22 创建动态分配的任务
static TCB_t* task_prv_pxCreateTask(TaskFunc_t pxTaskCode, const char* const pcName, const cfgSTACK_DEPTH_TYPE u32StackDepth,
void* const pvParams, UBaseType_t ulPrio, TaskHandle_t* const ppxCreatedTask);
// 3.23 任务创建时的额外初始化(如果定义了相关宏)
// 3.24 被动空闲钩子函数(外部声明)
// 3.25 处理 snprintf 返回值
// ========================
// 十一. 函数实现
// ========================
// 11.1 3.6 初始化任务链表
static void task_prv_vInitTaskLists(void){
UBaseType_t ulPrio;
// 1. 初始化就绪任务链表
for(ulPrio = 0; ulPrio < (UBaseType_t)cfgMAX_PRIO; ulPrio++){
list_vInit(&(g_pxReadyTasksLists[ulPrio]));
}
// 2. 初始化延时任务链表1
list_vInit(&g_xDelayedTaskList1);
// 3. 初始化延时任务链表2
list_vInit(&g_xDelayedTaskList2);
// 4. 初始化挂起就绪任务链表
list_vInit(&g_xPendingReadyList);
// 5. 初始化等待终止任务链表
list_vInit(&g_xTasksWaitingTermination);
// 6. 初始化挂起任务链表
list_vInit(&g_xSuspendedTaskList);
// 7. 初始化当前使用的延时任务链表指针
g_pxDelayedTaskList = &g_xDelayedTaskList1;
// 8. 初始化溢出延时任务链表指针
g_pxOverflowDelayedTaskList = &g_xDelayedTaskList2;
}
// 11.2 3.17 初始化新创建的任务控制块
static void task_prv_vInitTCB(TaskFunc_t pxTaskCode, const char* const pcName, const cfgSTACK_DEPTH_TYPE u32StackDepth,
void* const pvParameters, UBaseType_t ulPrio, TaskHandle_t* const ppxCreatedTask,
TCB_t* pxNewTCB, const MemRegion_t* pxRegions){
StackType_t* pu32TopOfStack;
// 1. MPU相关
// 2. 栈初始化填充(便于调试)
// 3. 计算栈顶地址
#if ( portSTACK_GROWTH < 0 )
// 1. 计算未对齐的初始栈顶,pu32BaseOfStack 为系统分配的栈底指针
pu32TopOfStack = &(pxNewTCB->pu32BaseOfStack[u32StackDepth - 1]);
// 2. 对齐到最近的低地址(向下对齐,把地址向下舍入到 8 的倍数)
pu32TopOfStack = (StackType_t*) (((portPOINTER_SIZE_TYPE)pu32TopOfStack) & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK)));
// 3. 断言检查是否对齐
#endif
// 4. 设置任务名称
if( pcName != NULL ){
for(UBaseType_t ulx = ( UBaseType_t ) 0; ulx < ( UBaseType_t ) cfgMAX_TASK_NAME_LEN; ulx++ ){
pxNewTCB->pcName[ ulx ] = pcName[ ulx ];
if( pcName[ ulx ] == ( char ) 0x00 ) break;
}
pxNewTCB->pcName[ cfgMAX_TASK_NAME_LEN - 1U ] = '\0';
}
// 5. 设置任务优先级
if( ulPrio >= ( UBaseType_t ) cfgMAX_PRIO ){
ulPrio = ( UBaseType_t ) cfgMAX_PRIO - ( UBaseType_t ) 1U;
}
pxNewTCB->ulPrio = ulPrio;
// 6. 列表节点初始化
list_vInitNode( &( pxNewTCB->xStateListNode ) );
list_vInitNode( &( pxNewTCB->xEventListNode ) );
listSET_NODE_OWNER( &( pxNewTCB->xStateListNode ), pxNewTCB );
listSET_NODE_VAL( &( pxNewTCB->xEventListNode ), ( TickType_t ) cfgMAX_PRIO - ( TickType_t ) ulPrio );
listSET_NODE_OWNER( &( pxNewTCB->xEventListNode ), pxNewTCB );
// 7. MPU设置
// 8. TLS块初始化
// 9. 栈初始化
// 10. 任务状态和属性初始化
// 11. 任务句柄设置
if( ppxCreatedTask != NULL ){
*ppxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
}
// 11.3 3.18 将新任务添加到就绪任务列表中
#if ( cfgNUM_CORES == 1 )
static void task_prv_vAddNewTaskToReadyList(TCB_t* pxNewTCB){
taskENTER_CRITICAL();
{
g_ulCurTasksNum = ( UBaseType_t ) ( g_ulCurTasksNum + 1U );
if( g_pxCurTCB == NULL ){
g_pxCurTCB = pxNewTCB;
if( g_ulCurTasksNum == ( UBaseType_t ) 1 ) task_prv_vInitTaskLists();
}else{
if( g_lSchedulerRunning == pdFALSE ){
if( g_pxCurTCB->ulPrio <= pxNewTCB->ulPrio ) g_pxCurTCB = pxNewTCB;
}
}
g_ulTaskNum++;
prvAddTaskToReadyList( pxNewTCB );
portSETUP_TCB( pxNewTCB );
}
taskEXIT_CRITICAL();
if( g_lSchedulerRunning != pdFALSE ) taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxNewTCB );
}
#endif
// 11.4 3.22
static TCB_t* task_prv_pxCreateTask(TaskFunc_t pxTaskCode, const char* const pcName, const cfgSTACK_DEPTH_TYPE u32StackDepth,
void* const pvParams, UBaseType_t ulPrio, TaskHandle_t* const ppxCreatedTask){
TCB_t* pxNewTCB;
StackType_t* pu32Stack;
pu32Stack = port_pvMallocStack((((size_t)u32StackDepth) * sizeof(StackType_t)));
if( pu32Stack != NULL ) {
pxNewTCB = (TCB_t*) port_pvMalloc(sizeof(TCB_t));
if( pxNewTCB != NULL )
pxNewTCB->pu32BaseOfStack = pu32Stack;
else
port_vFreeStack( pu32Stack );
}else{
pxNewTCB = NULL;
}
if( pxNewTCB != NULL )
task_prv_vInitTCB( pxTaskCode, pcName, u32StackDepth, pvParams, ulPrio, ppxCreatedTask, pxNewTCB, NULL );
return pxNewTCB;
}
//11.5 4.1 在堆上动态创建任务(无核亲和性)
BaseType_t task_lCreate(TaskFunc_t pfTaskFunc,
const char* const pcName, // 任务名称
const cfgSTACK_DEPTH_TYPE u32StackDepth, // 栈深度,单位:字(word)
void* const pvParams, // 传递给任务的参数指针
UBaseType_t ulPrio, // 任务优先级
TaskHandle_t* const ppCreatedTask ){ // 返回创建的任务句柄指针(二级指针)
TCB_t* pxNewTCB;
BaseType_t lReturn;
pxNewTCB = task_prv_pxCreateTask( pfTaskFunc, pcName, u32StackDepth, pvParams, ulPrio, ppCreatedTask );
if( pxNewTCB != NULL ){
task_prv_vAddNewTaskToReadyList( pxNewTCB );
lReturn = pdPASS;
}else{
lReturn = -1;
}
return lReturn;
}
// 11.50 3.1
static BaseType_t task_prv_lCreateIdleTasks(void){
BaseType_t lReturn = pdPASS;
BaseType_t lCoreID;
char pcIdleName[ cfgMAX_TASK_NAME_LEN ];
TaskFunc_t pfIdleTaskFunc = NULL;
BaseType_t lIdleTaskNameIdx;
for( lIdleTaskNameIdx = ( BaseType_t ) 0; lIdleTaskNameIdx < ( BaseType_t ) cfgMAX_TASK_NAME_LEN; lIdleTaskNameIdx++ ){
pcIdleName[ lIdleTaskNameIdx ] = cfgIDLE_TASK_NAME[ lIdleTaskNameIdx ];
if( pcIdleName[ lIdleTaskNameIdx ] == ( char ) 0x00 ) break;
}
for( lCoreID = ( BaseType_t ) 0; lCoreID < ( BaseType_t ) cfgNUM_CORES; lCoreID++ ){
pfIdleTaskFunc = prvIdleTask;
lReturn = task_lCreate(pfIdleTaskFunc, pcIdleName, cfgMIN_STACK_SIZE, (void*)NULL, portPRIVILEGE_BIT, &(g_pxIdleTaskHandles[lCoreID]));
if( lReturn == pdFAIL ) break;
}
return lReturn;
}
// 11.6 6.1
void task_vStartScheduler(void){
BaseType_t lReturn;
lReturn = task_prv_lCreateIdleTasks();
if( lReturn == pdPASS ) {
portDISABLE_INTERRUPTS();
g_xNextTaskUnblockTime = portMAX_DELAY;
g_lSchedulerRunning = pdTRUE;
g_xTickCount = ( TickType_t ) cfgINITIAL_TICK_COUNT;
( void ) port_lStartScheduler();
}
( void ) g_pxIdleTaskHandles;
( void ) g_ulTopUsedPriority;
}
// 11.7 8.8
#if ( cfgNUM_CORES == 1 )
void task_vSwitchContext( void ){
if( g_ulSchedulerSuspended != ( UBaseType_t ) 0U ){
g_lYieldPendings[ 0 ] = pdTRUE;
}else{
g_lYieldPendings[ 0 ] = pdFALSE;
taskSELECT_HIGHEST_PRIORITY_TASK();
portTASK_SWITCH_HOOK( g_pxCurTCB );
}
}
#endif
// 11.8 3.15
static void task_prv_vResetNxtTaskUnblockTime(void){
if( listLIST_IS_EMPTY( g_pxDelayedTaskList ) != pdFALSE ){
g_xNextTaskUnblockTime = portMAX_DELAY;
}else{
g_xNextTaskUnblockTime = listGET_HEAD_VAL( g_pxDelayedTaskList );
}
}
// 11.9 8.2
BaseType_t task_lIncrementTick(void){
TCB_t* pxTCB;
TickType_t xItemValue;
BaseType_t lSwitchRequired = pdFALSE;
if( g_ulSchedulerSuspended == ( UBaseType_t ) 0U ){
const TickType_t xConstTickCount = g_xTickCount + ( TickType_t ) 1;
g_xTickCount = xConstTickCount;
if( xConstTickCount == ( TickType_t ) 0U ) taskSWITCH_DELAYED_LISTS();
if( xConstTickCount >= g_xNextTaskUnblockTime ) {
for( ; ; ){
if( listLIST_IS_EMPTY( g_pxDelayedTaskList ) != pdFALSE ){
g_xNextTaskUnblockTime = portMAX_DELAY;
break;
} else{
pxTCB = listGET_OWNER_OF_HEAD( g_pxDelayedTaskList );
xItemValue = listGET_NODE_VAL( &( pxTCB->xStateListNode ) );
if( xConstTickCount < xItemValue ){
g_xNextTaskUnblockTime = xItemValue;
break;
}
listRM_NODE( &( pxTCB->xStateListNode ) );
if(listGET_NODE_CONTAINER( &( pxTCB->xEventListNode ) ) != NULL) listRM_NODE(&( pxTCB->xEventListNode));
prvAddTaskToReadyList( pxTCB );
if( pxTCB->ulPrio > g_pxCurTCB->ulPrio ) lSwitchRequired = pdTRUE;
}
}
}
if( listCUR_LIST_LEN( &( g_pxReadyTasksLists[ g_pxCurTCB->ulPrio ] ) ) > 1U ) lSwitchRequired = pdTRUE;
if( g_lYieldPendings[ 0 ] != pdFALSE ) lSwitchRequired = pdTRUE;
}else{
g_xPendedTicks += 1U;
}
return lSwitchRequired;
}
// 11.10 3.7
static portTASK_FUNCTION( prvIdleTask, pvParameters ){
( void ) pvParameters;
for( ; cfgCONTROL_INFINITE_LOOP(); ) {
if( listCUR_LIST_LEN( &( g_pxReadyTasksLists[ tskIDLE_PRIO ] ) ) > ( UBaseType_t ) cfgNUM_CORES ){
taskYIELD();
}
}
}
// 11.11 5.1
void task_vDelay(const TickType_t xTicksToDelay){
BaseType_t lAlreadyYielded = pdFALSE;
if( xTicksToDelay > ( TickType_t ) 0U ){
task_vSuspendAll();
{
task_prv_vAddCurTaskToDelayedList(xTicksToDelay, pdFALSE );
}
lAlreadyYielded = task_lResumeAll();
}
if( lAlreadyYielded == pdFALSE ){
taskYIELD_WITHIN_API();
}
}
// 11.13 6.3
void task_vSuspendAll(void){
portSOFTWARE_BARRIER();
g_ulSchedulerSuspended = ( UBaseType_t ) ( g_ulSchedulerSuspended + 1U );
portMEMORY_BARRIER();
}
// 11.14 6.4
BaseType_t task_lResumeAll(void){
TCB_t * pxTCB = NULL;
BaseType_t lAlreadyYielded = pdFALSE;
{
taskENTER_CRITICAL();
{
BaseType_t lCoreID;
lCoreID = ( BaseType_t ) portGET_CORE_ID();
cfgASSERT( g_ulSchedulerSuspended != 0U );
g_ulSchedulerSuspended = ( UBaseType_t ) ( g_ulSchedulerSuspended - 1U );
if( g_ulSchedulerSuspended == ( UBaseType_t ) 0U ){
if( g_ulCurTasksNum > ( UBaseType_t ) 0U ){
while( listLIST_IS_EMPTY( &g_xPendingReadyList ) == pdFALSE ){
pxTCB = listGET_OWNER_OF_HEAD( ( &g_xPendingReadyList ) );
listRM_NODE( &( pxTCB->xEventListNode ) );
portMEMORY_BARRIER();
listRM_NODE( &( pxTCB->xStateListNode ) );
prvAddTaskToReadyList( pxTCB );
if( pxTCB->ulPrio > g_pxCurTCB->ulPrio ) g_lYieldPendings[ lCoreID ] = pdTRUE;
}
if( pxTCB != NULL ) task_prv_vResetNxtTaskUnblockTime();
{
TickType_t xPendedCounts = g_xPendedTicks;
if( xPendedCounts > ( TickType_t ) 0U ){
do{
if( task_lIncrementTick() != pdFALSE ) g_lYieldPendings[ lCoreID ] = pdTRUE;
--xPendedCounts;
} while( xPendedCounts > ( TickType_t ) 0U );
g_xPendedTicks = 0;
}
}
if( g_lYieldPendings[ lCoreID ] != pdFALSE ){
lAlreadyYielded = pdTRUE;
taskYIELD_TASK_CORE_IF_USING_PREEMPTION( g_pxCurTCB );
}
}
}
}
taskEXIT_CRITICAL();
}
return lAlreadyYielded;
}
// 11.15 3.10
static void task_prv_vAddCurTaskToDelayedList(TickType_t xTicksToWait, const BaseType_t lCanBlockIndefinitely){
TickType_t xTimeToWake;
const TickType_t xConstTickCount = g_xTickCount;
List_t* const pxDelayedList = g_pxDelayedTaskList;
List_t* const pxOverflowDelayedList = g_pxOverflowDelayedTaskList;
if( list_lRmNode( &( g_pxCurTCB->xStateListNode ) ) == ( UBaseType_t ) 0 ){
portRESET_READY_PRIORITY( g_pxCurTCB->ulPrio, g_ulTopReadyPriority );
}
xTimeToWake = xConstTickCount + xTicksToWait;
listSET_NODE_VAL( &( g_pxCurTCB->xStateListNode ), xTimeToWake );
if( xTimeToWake < xConstTickCount ){
list_vInsertSorted( pxOverflowDelayedList, &( g_pxCurTCB->xStateListNode ) );
}else{
list_vInsertSorted( pxDelayedList, &( g_pxCurTCB->xStateListNode ) );
if( xTimeToWake < g_xNextTaskUnblockTime ) g_xNextTaskUnblockTime = xTimeToWake;
}
( void ) lCanBlockIndefinitely;
}