Detailed explanation of FreeRTOS task scheduling and related functions (2)


1. Task creation function 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 ) *\ (1)
	sizeof( StackType_t ) ) ); 
	if( pxStack != NULL )
	{
    
    
		pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); (2)
		if( pxNewTCB != NULL )
		{
    
    
			pxNewTCB->pxStack = pxStack; (3)
		}
		else
		{
    
    
			vPortFree( pxStack ); (4)
		}
	}
	else
	{
    
    
		pxNewTCB = NULL;
	}
	if( pxNewTCB != NULL )
	{
    
    
		#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
		{
    
    
			pxNewTCB->ucStaticallyAllocated =\ (5)
			tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
		}
		#endif /* configSUPPORT_STATIC_ALLOCATION */
	prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, \ (6)
	pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
	prvAddNewTaskToReadyList( pxNewTCB ); (7)
	xReturn = pdPASS;
	}
	else
	{
    
    
		xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
	}
	return xReturn;
}

(1). Use the function pvPortMalloc() to apply for memory for the task stack of the task. When applying for memory, byte alignment will be performed.

(2) If the memory application for the stack is successful, apply for memory for the task control block, and use the function pvPortMalloc() as well.

(3) If the memory application of the task control block is successful, the task stack field pxStack in the memory control block is initialized, and the task stack applied in (1) is used.

(4) If the memory application of the task control block fails, release the memory of the previously successfully applied task stack.

(5) The marked task stack and task control block are obtained by using a dynamic memory allocation method.

(6) Use the function prvInitialiseNewTask() to initialize the task. This function completes the initialization of each field in the task control block!

(7) Use the function prvAddNewTaskToReadyList() to add the newly created task to the ready list.

2. Task initialization function 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 ) 
{
    
    
	StackType_t *pxTopOfStack;
	UBaseType_t x;
	#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY ==1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) )
	{
    
    
		( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE,\ (1)
		( size_t ) ulStackDepth * sizeof( StackType_t ) );
	}
	#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 /* configUSE_MUTEXES */
	vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); (8)
	vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); (9)
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); (10)
	listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), \ (11)
	( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); 
	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
	{
    
    
		_REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
	}
	#endif
	#if( INCLUDE_xTaskAbortDelay == 1 ) //使能函数 xTaskAbortDelay()
	{
    
    
		pxNewTCB->ucDelayAborted = pdFALSE;
	}
	#endif
	pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode,\ (13) 
	 pvParameters );
	if( ( void * ) pxCreatedTask != NULL )
	{
    
    
		*pxCreatedTask = ( TaskHandle_t ) pxNewTCB; (14)
	}
	else
	{
    
    
		mtCOVERAGE_TEST_MARKER();
	}
}

(1) If the stack overflow detection function or tracking function is enabled, use a fixed value tskSTACK_FILL_BYTE to fill the task stack, and this value is 0xa5U.

(2) Calculate the pxTopOfStack at the top of the stack, which will be used when initializing the stack later.

(3) Save the task name of the task.

(4) Add the string terminator '\0' to the task name array.

(5) Determine whether the task priority is legal. If the set task priority is greater than configMAX_PRIORITIES, modify the priority to configMAX_PRIORITIES-1.

(6) Initialize the priority field uxPriority of the task control block.

(7) When the mutual exclusion semaphore function is enabled, the corresponding fields need to be initialized.

(8) and (9), initialize the list items xStateListItem and xEventListItem, there are two list items in the task control block structure, these two list items are initialized here.

(10) and (12), set the list items xStateListItem and xEventListItem to the task control block of the current task, that is, set the field pvOwner of these two list items to the task control block of the newly created task.

(11) Set the field xItemValue of the list item xEventListItem to configMAX_PRIORITIES-uxPriority, for example, the current task priority is 3, and the maximum priority is 32, then xItemValue is 32-3=29, which means that the larger the value of xItemValue, the higher the priority smaller. When we learned about lists and list items in the last chapter, we said that the insertion of the list is in ascending order according to the value of xItemValue.

(12) Initialize the thread local storage pointer, if this function is enabled.

(13) Call the function pxPortInitialiseStack() to initialize the task stack.

(14) Generate a task handle and return it to the parameter pxCreatedTask, from which it can be seen that the task handle is actually the task control block

3. Task stack initialization function pxPortInitialiseStack()

StackType_t *pxPortInitialiseStack( StackType_t * pxTopOfStack, TaskFunction_t pxCode, 
void * pvParameters )
{
    
    
	pxTopOfStack--;
	*pxTopOfStack = portINITIAL_XPSR; (1)
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; (2)
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError; (3)
	pxTopOfStack -= 5; (4)
	*pxTopOfStack = ( StackType_t ) pvParameters; (5)
	pxTopOfStack -= 8; (6)
	return pxTopOfStack;
}

The stack is used to save the scene during context switching. Generally, after a new stack is created, it will be initialized first, that is, some registers of the Cortex-M core will be assigned initial values. These initial values ​​are saved in the task stack, and the order of saving is as follows: xPSR, R15 (PC), R14 (LR), R12, R3 R0 , R11 R14.

(1) The value of the register xPSR is portINITIAL_XPSR, and its value is 0x01000000. xPSR is a core register of Cortex-M, called the program status register, 0x01000000 means that the bit24 of this register is 1, which means it is in Thumb state, that is, the Thumb instruction used.

(2) The register PC is initialized as the task function pxCode.

(3) The register LR is initialized as the function prvTaskExitError.

(4), skip 4 registers, R12, R3, R2, R1, these four registers are not initialized.

(5) Register R0 is initialized as pvParameters. In general, function calls will use R0~R3 as input parameters, and R0 can also be used as the return result. If the return value is 64 bits, R1 will also be used to return the result. Here The pvParameters are the parameters of the task function and are stored in the register R0.

(6) Skip 8 registers, R11, R10, R8, R7, R6, R5, R4.
After the above initialization, the stack result at this time is shown in the figure below:
insert image description here
In the figure, STM32 is taken as an example, and the stack is in a downward growth mode.

4. Add tasks to the ready list prvAddNewTaskToReadyList()

After the task is created, it will be added to the ready list. FreeRTOS uses different lists to represent the different states of the task. Multiple lists are defined in the file tasks.c to complete different functions. These lists are as follows:

PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
PRIVILEGED_DATA static List_t xDelayedTaskList1;
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;

The list array pxReadyTasksLists[] is the task ready list. The size of the array is configMAX_PRIORITIES, that is to say, one list per priority, so that tasks with the same priority use one list. Adding a newly created task to the ready list is done by the function prvAddNewTaskToReadyList(), the function is as follows:

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
    
    
	taskENTER_CRITICAL();
	{
    
    
		uxCurrentNumberOfTasks++; (1)
		if( pxCurrentTCB == NULL )//正在运行任务块为 NULL,说明没有任务运行!
		{
    
    
			pxCurrentTCB = pxNewTCB;//将新任务的任务控制块赋值给 pxCurrentTCB
			//新创建的任务是第一个任务!!!
		if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
		{
    
    
			prvInitialiseTaskLists(); (2)
		}
		else
		{
    
    
			mtCOVERAGE_TEST_MARKER();
		}
	}
	else
	{
    
    
		if( xSchedulerRunning == pdFALSE )
		{
    
    
			 //新任务的任务优先级比正在运行的任务优先级高。
			if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
			{
    
    
				pxCurrentTCB = pxNewTCB; (3)
			}
			else
			{
    
    
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
    
    
			mtCOVERAGE_TEST_MARKER();
		}
	}
	uxTaskNumber++; //uxTaskNumber 加一,用作任务控制块编号。
	#if ( configUSE_TRACE_FACILITY == 1 )
	{
    
    
		pxNewTCB->uxTCBNumber = uxTaskNumber;
	}
	#endif /* configUSE_TRACE_FACILITY */
	prvAddTaskToReadyList( pxNewTCB ); (4)
	}
	taskEXIT_CRITICAL();
	if( xSchedulerRunning != pdFALSE )
	{
    
    
		 //新任务优先级比正在运行的任务优先级高
		if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
		{
    
    
			taskYIELD_IF_USING_PREEMPTION(); (5)
		}
		else
		{
    
    
			mtCOVERAGE_TEST_MARKER();
		}
	}
	else
	{
    
    
		mtCOVERAGE_TEST_MARKER();
	}
}

(1) The variable uxCurrentNumberOfTasks is a global variable used to count the number of tasks.

(2) The variable uxCurrentNumberOfTasks is 1, indicating that the task being created is the first task! Then you need to initialize the corresponding list first, and initialize the corresponding list by calling the function prvInitialiseTaskLists().

(3) The priority of the newly created task is higher than that of the running task, so it is necessary to modify pxCurrentTCB as the task control block of the newly created task.

(4) Call the function prvAddTaskToReadyList() to add the task to the ready list. This is actually a macro, as follows:

#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 )

Among them, the macro portRECORD_READY_PRIORITY() is used to record the tasks in the ready state, which is realized by operating the global variable uxTopReadyPriority. This variable is used to find the task with the highest priority in the ready state. The specific operation process will be explained later when task switching is explained. Next use the function vListInsertEnd() to add tasks to the end of the ready list.

(5) If the task priority of the new task is the highest, and the scheduler has started to run normally, then call the function taskYIELD_IF_USING_PREEMPTION() to complete a task switch.

5. Task delete vTaskDelete()

We have already learned how to use the task deletion function vTaskDelete() of FreeRTOS. In this section, we will study the specific implementation process of the function vTaskDelete() in detail. The source code of the function is as follows:

void vTaskDelete( TaskHandle_t xTaskToDelete )
{
    
    
	TCB_t *pxTCB;
	taskENTER_CRITICAL();
	{
    
    
		//如果参数为 NULL 的话那么说明调用函数 vTaskDelete()的任务要删除自身。
		pxTCB = prvGetTCBFromHandle( xTaskToDelete ); (1)
		//将任务从就绪列表中删除。
		if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) (2)
		{
    
    
			taskRESET_READY_PRIORITY( pxTCB->uxPriority );
		}
		else
		{
    
    
			mtCOVERAGE_TEST_MARKER();
		}
	//任务是否在等待某个事件?
		if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) (3)
		{
    
    
			( void ) uxListRemove( &( pxTCB->xEventListItem ) );
		}
		else
		{
    
    
			mtCOVERAGE_TEST_MARKER();
		}
		uxTaskNumber++;
		if( pxTCB == pxCurrentTCB ) (4)
		{
    
    
			vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->\ (5)
xStateListItem ) );
			++uxDeletedTasksWaitingCleanUp; (6)
			portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending ); (7)
		}
		else
		{
    
    
			--uxCurrentNumberOfTasks; (8)
			prvDeleteTCB( pxTCB ); (9)
			prvResetNextTaskUnblockTime(); (10)
		}
		traceTASK_DELETE( pxTCB );
	}
	taskEXIT_CRITICAL();
	//如果删除的是正在运行的任务那么就需要强制进行一次任务切换。
	if( xSchedulerRunning != pdFALSE )
	{
    
    
		if( pxTCB == pxCurrentTCB )
		{
    
    
			configASSERT( uxSchedulerSuspended == 0 );
			portYIELD_WITHIN_API(); (11)
		}
		else
		{
    
    
			mtCOVERAGE_TEST_MARKER();
		}
	}
}	

(1) Call the function prvGetTCBFromHandle() to obtain the task control block of the task to be deleted, and the parameter is the task handle. If the parameter is the currently executing task handle then the return value is NULL.

(2) Delete the task from the task ready list.

(3) Check whether the task is waiting for an event (such as a semaphore, queue, etc.), because if the task is waiting for an event, the task will be placed in the corresponding list, and it needs to be deleted from the corresponding list Lose.

(4), the task to be deleted is currently running

(5) To delete a task, the memory occupied by the task's task control block and task stack must be released (if the task is created using a dynamic method), but the current task is running, obviously the memory of the task control block and task stack Memory cannot be freed immediately! The corresponding memory must be released after the current task is completed, so a "mark" is required to mark that there is a task that needs to be processed. Here, the current task is added to the list xTasksWaitingTermination, if there is a task to delete itself, it will be added to the list xTasksWaitingTermination. So here comes the question? Where is memory release done? Idle tasks! The idle task will release all the memory that needs to be released in turn.

(6), uxDeletedTasksWaitingCleanUp is a global variable used to record how many tasks need to release memory.

(7) Call the task deletion hook function, and the specific content of the hook function needs to be implemented by the user.

(8) To delete other tasks, the variable uxCurrentNumberOfTasks is reduced by one, that is, the number of current tasks is reduced by one.

(9) Because it is to delete other tasks, the function prvDeleteTCB() can be called directly to delete the task control block.

(10) Recalculate how long it will take to execute the next task, that is, the unlocking time of the next task, to prevent the unlocking time of a task from referring to the task that was just deleted.

(11) If the task being deleted is a running task, a task switch must be forced after the deletion.

Six, task suspension vTaskSuspend()

The function vTaskSuspend() is used to suspend the task. The source code of the function is as follows:

void vTaskSuspend( TaskHandle_t xTaskToSuspend )
{
    
    
	TCB_t *pxTCB;
	taskENTER_CRITICAL();
	{
    
    
		//如果参数为 NULL 的话说明挂起自身
		pxTCB = prvGetTCBFromHandle( xTaskToSuspend ); (1)
		traceTASK_SUSPEND( pxTCB );
		//将任务从就绪或者延时列表中删除,并且将任务放到挂起列表中
		if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) (2)
		{
    
    
			taskRESET_READY_PRIORITY( pxTCB->uxPriority );
		}
		else
		{
    
    
			mtCOVERAGE_TEST_MARKER();
		}
		//任务是否还在等待其他事件
		if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) (3)
		{
    
    
			( void ) uxListRemove( &( pxTCB->xEventListItem ) );
		}
		else
		{
    
    
			mtCOVERAGE_TEST_MARKER();
		}
		vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); (4)
	}
	taskEXIT_CRITICAL();
	if( xSchedulerRunning != pdFALSE )
	{
    
    
		taskENTER_CRITICAL();
		{
    
    
			prvResetNextTaskUnblockTime(); (5)
		}
		taskEXIT_CRITICAL();
	}
	else
	{
    
    
		mtCOVERAGE_TEST_MARKER();
	}
	if( pxTCB == pxCurrentTCB )
	{
    
    
		if( xSchedulerRunning != pdFALSE )
		{
    
    
			configASSERT( uxSchedulerSuspended == 0 );
			portYIELD_WITHIN_API(); (6)
		}
		else
		{
    
    
			if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) ==\ (7)
 uxCurrentNumberOfTasks )
			{
    
    
				pxCurrentTCB = NULL; (8)
			}
			else
			{
    
    
				vTaskSwitchContext(); (9)
			}
		}
	}
	else
	{
    
    
	mtCOVERAGE_TEST_MARKER();
	}
}
		

(1) Obtain the task control block of the task to be deleted through the function prvGetTCBFromHandle().

(2) Delete the task from the task ready list delay list.

(3) Check whether the task is waiting for a certain event (such as a semaphore, queue, etc.), and if the task is still waiting for a certain event, delete it from the corresponding event list.

(4) Add the task to the end of the suspended task list. The suspended task list is xSuspendedTaskList, and all suspended tasks will be put into this list.

(5) Recalculate how long it will take to execute the next task, that is, the unlocking time of the next task. Prevents the unlock time of a task from referencing the task that was just suspended.

(6) If the task that has just been suspended is a running task, and the task scheduler is running normally, then it is necessary to call the function portYIELD_WITHIN_API() to force a task switch.

(7), pxCurrentTCB points to the running task, but the running task will be suspended, so it is necessary to find another "object" for pxCurrentTCB. That is to find the next task to be run. Originally, this work is done by the task switching function, but when the program runs to this line, it means that the task scheduler is suspended, and the task switching function is powerless. You must manually find the next task. Run the task again. Call the function listCURRENT_LIST_LENGTH() to judge whether all tasks in the system are suspended, that is, check whether the length of the list xSuspendedTaskList is equal to
uxCurrentNumberOfTasks. If it is equal, it means that all tasks in the system are suspended (in fact, this does not exist, because at least one idle task can run, and no blocking or suspending idle tasks will be called during the execution of idle tasks. The API function of the task is to ensure that there is always a runnable task in the system).

(8) If all tasks are suspended, pxCurrentTCB can only be equal to NULL, so that when a new task is created, pxCurrentTCB can point to this new task.

(9) If there are other unsuspended tasks, call vTaskSwitchContext() to get the next task to run.

7. Task recovery vTaskResume()

The task recovery function has two vTaskResume() and xTaskResumeFromISR(), one is used in the task, the other is used in the interrupt, but the basic processing is the same, let's take the function vTaskResume() as an example Explain the detailed process of task recovery.

void vTaskResume( TaskHandle_t xTaskToResume )
{
    
    
	TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume; (1)
	configASSERT( xTaskToResume );
	//函数参数不可能为 NULL。
	if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) ) (2)
	{
    
    
		taskENTER_CRITICAL(); (3)
		{
    
    
			if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) (4)
			{
    
    
				traceTASK_RESUME( pxTCB );
				( void ) uxListRemove( &( pxTCB->xStateListItem ) ); (5)
				prvAddTaskToReadyList( pxTCB ); (6)
					if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) (7)
					{
    
    
						taskYIELD_IF_USING_PREEMPTION(); (8)
					}
					else
					{
    
    
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
    
    
					mtCOVERAGE_TEST_MARKER();
				}
			}
			taskEXIT_CRITICAL(); (9)
		}
	else
	{
    
    
		mtCOVERAGE_TEST_MARKER();
	}
}

(1) Obtain the task control block of the task to be restored according to the parameters. Because there is no such thing as restoring the running task, the parameter cannot be NULL (you can't force a NULL parameter), here There is no need to use the function prvGetTCBFromHandle() to obtain the task control block to be restored, and prvGetTCBFromHandle() will handle the case where the parameter is NULL.

(2) The task control block cannot be NULL and pxCurrentTCB, because there is no way to restore the currently running task.

(3), call the function taskENTER_CRITICAL() to enter the critical section

(4) Call the function prvTaskIsTaskSuspended() to determine whether the task to be resumed has been suspended before, and the resumed task must be the suspended task. If it is not suspended, it does not need to be resumed.

(5) First, delete the task to be resumed from the original list, and after the task is suspended, it will be put into the task suspension list xSuspendedTaskList.

(6). Add the task to be restored to the list of ready tasks.

(7) The priority of the task to be restored is higher than that of the currently running task.

(8) Because the task to be restored has the highest priority, it is necessary to call the function
taskYIELD_IF_USING_PREEMPTION() to complete a task switch.

(9) Call the function taskEXIT_CRITICAL() to exit the critical section.

Guess you like

Origin blog.csdn.net/Dustinthewine/article/details/130058543