IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> FreeRTOS学习第二篇——FreeRTOS任务创建(上) -> 正文阅读

[嵌入式]FreeRTOS学习第二篇——FreeRTOS任务创建(上)

声明:本文为博主的学习篇章,欢迎大家指错,共同学习
在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();
	}
}

这里面有太多东西需要了解了,并且这里面涉及到了调度器的知识,所以我打算把这个放到下一篇去。
到这里任务的创建就完成了。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-02-28 15:45:41  更:2022-02-28 15:46:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 8:22:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码