求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
要资料
 
追随技术信仰

随时听讲座
每天看新闻
 
 
FreeRTOS源码分析及应用开发
1. FreeRTOS系列 | FreeRTOS简介
2. FreeRTOS系列 | 开发环境
3. FreeRTOS系列 | 任务基础知识
4. FreeRTOS系列 | 任务创建和删除
5. FreeRTOS系列 | 任务挂起和恢复
6. FreeRTOS系列|多任务调度
7. FreeRTOS系列|时间管理
8. FreeRTOS系列|中断管理和临界段
9. FreeRTOS系列|任务堆栈
10. FreeRTOS系列 | 处理器利用率
11. FreeRTOS系列|任务相关API函数
12. FreeRTOS系列 | 列表和列表项
13. FreeRTOS系列|消息队列一
14. FreeRTOS系列|消息队列二
15. FreeRTOS系列 | 二值信号量
16. FreeRTOS系列 | 互斥信号量
17. FreeRTOS系列 | 计数信号量
18. FreeRTOS系列 | 递归互斥信号量
19. FreeRTOS系列 | 事件标志组
20. FreeRTOS系列 | 软件定时器
21. FreeRTOS系列 | 低功耗管理
22. FreeRTOS系列 | 内存管理一
23. FreeRTOS系列 | 内存管理二
 

 
目录
FreeRTOS系列|任务创建和删除
作者:安迪西嵌入式
270 次浏览
4次  

任务创建和删除

1. 任务创建和删除API函数

xTaskCreate()函数:动态创建一个新的任务,每个任务都需要RAM来保存任务状态(任务控制块+任务栈),此接口采用动态分配内存资源

BaseType_t xTaskCreate(TaskFunction_t pvTaskCode,	//任务函数(函数名)
                       const char *const pcName,	//任务名称(字符串)
                       unsigned short usStackDepth,	//任务堆栈大小
                       void *pvParameters,			//传递给任务函数的参数
                       UBaseType_t uxPriority,		//任务优先级
                       TaskHandle_t *pxCreatedTask);//任务句柄
返回值:pdPASS:创建成功
	   errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:堆空间不足,失败
/*注意:configSUPPORT_DYNAMIC_ALLOCATION 必须置为1*/

xTaskCreateStatic()函数:静态创建一个新的任务,每个任务都需要RAM来保存任务状态(任务控制块+任务栈),此接口采用静态分配内存资源

TaskHandle_t xTaskCreateStatic(TaskFunction_t pvTaskCode,//任务函数(函数名)
                               const char *const pcName, //任务名称(字符串)
                               uint32_t ulStackDepth,	 //任务堆栈大小
                               void *pvParameters,		 //传递给任务函数的参数
                               UBaseType_t uxPriority,	 //任务优先级
                               StackType_t *const puxStackBuffer, //任务堆栈
                               StaticTask_t *const pxTaskBuffer);//任务控制块
返回值:NULL:任务创建失败
	   其他值:任务句柄
/*注意:configSUPPORT_STATIC_ALLOCATION 必须置为1*/

任务删除函数

函数原型:void vTaskDelete(TaskHandle_t xTaskToDelete)
参    数:xTaskToDelete 要删除的任务的任务句柄
返 回 值:无

2. 任务创建和删除函数源码分析

2.1 FreeRTOS任务创建函数源码分析

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;
			
	#define portSTACK_GROWTH	//(-1)表示满减栈
	#if( portSTACK_GROWTH > 0 ){
	}
	#else{ /* portSTACK_GROWTH */
		StackType_t *pxStack;
		/* 任务栈内存分配*/
		pxStack = ( StackType_t *) pvPortMalloc(((( size_t) usStackDepth ) * sizeof( StackType_t))); 
		if( pxStack != NULL ){
			/* 任务控制块内存分配 */
			pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 
			if( pxNewTCB != NULL ){
				/* 赋值栈地址 */
				pxNewTCB->pxStack = pxStack;
			}
			else{
				/* 释放栈空间 */
				vPortFree( pxStack );
			}
		}
		else{
			/* 没有分配成功 */
			pxNewTCB = NULL;
		}
	}
	#endif /* portSTACK_GROWTH */

	if( pxNewTCB != NULL )
	{
		/* 新建任务初始化 */
		prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
		/* 把任务添加到就绪列表中 */
		prvAddNewTaskToReadyList( pxNewTCB );
		xReturn = pdPASS;
	}
	else{
		xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
	}

	return xReturn;
}

2.2 任务初始化函数源码分析

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( portSTACK_GROWTH < 0 ){
		/* 把栈空间的高地址分配给栈顶 */
		pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
		/* 栈对齐----栈要8字节对齐 */
		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));
	}
	#else /* portSTACK_GROWTH */
	{
	}
	#endif /* portSTACK_GROWTH */

	/* 存储任务名称 */
	for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ){
		pxNewTCB->pcTaskName[ x ] = pcName[ x ];

		if( pcName[ x ] == 0x00 ){
			break;
		}
		else{
			mtCOVERAGE_TEST_MARKER();
		}
	}

	/* \0补齐字符串 */
	pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
	/* 判断任务分配的优先级,是否大于最大值  如果超过最大值,赋值最大值 */
	if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ){
		uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
	}
	else{
		mtCOVERAGE_TEST_MARKER();
	}
	/* 赋值任务优先级到任务控制块 */
	pxNewTCB->uxPriority = uxPriority;
	/* 任务状态表 事件表初始化 */
	vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
	vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
	/* 任务控制块链接到任务状态表中 */
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
	/* 任务控制块连接到事件表中 */
	listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); 
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );

	#if( portUSING_MPU_WRAPPERS == 1 ){
	
	}
	#else{ /* portUSING_MPU_WRAPPERS */
		/* 任务堆栈初始化,之后返回任务栈顶 */
		pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
	}
	#endif /* portUSING_MPU_WRAPPERS */

	if( ( void * ) pxCreatedTask != NULL ){
		/* 赋值任务句柄 */
		*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	}
	else{
		mtCOVERAGE_TEST_MARKER();
	}
}

2.3 任务堆栈初始化函数源码分析

StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters){
	pxTopOfStack--;		/* 入栈程序状态寄存器 */
	*pxTopOfStack = portINITIAL_XPSR;	/* xPSR */
	
	pxTopOfStack--;		/* 入栈PC指针 */
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;	/* PC */
	
	pxTopOfStack--;		/* 入栈LR链接寄存器 */
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;	/* LR */
	
	pxTopOfStack -= 5;	/* 跳过R12, R3, R2 and R1这四个寄存器,不初始化 */
	*pxTopOfStack = ( StackType_t ) pvParameters;	/* R0作为传参入栈 */
	
	pxTopOfStack--;		/* 异常返回值入栈   返回值是确定程序使用的栈地址是哪一个 MSP PSP*/
	*pxTopOfStack = portINITIAL_EXEC_RETURN;
	
	pxTopOfStack -= 8;	/* 跳过R11, R10, R9, R8, R7, R6, R5 and R4这8个寄存器,不初始化 */
	return pxTopOfStack;	/*最终返回栈顶*/
}

以STM32(堆栈为向下增长模式)为例,经过上面的初始化后,此时的堆栈结果如下图所示

2.4 任务删除函数源码分析

void vTaskDelete( TaskHandle_t xTaskToDelete ){
	TCB_t *pxTCB;
	/* 进入临界段 */
	taskENTER_CRITICAL();
	{
		/* 如果传入的参数为NULL,说明调用vTaskDelete的任务要删除自身 */
		pxTCB = prvGetTCBFromHandle( xTaskToDelete );
		/* 将任务从就绪列表中移除 */
		if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){
			taskRESET_READY_PRIORITY( pxTCB->uxPriority );
		}
		else{
			mtCOVERAGE_TEST_MARKER();
		}
		/* 查看任务是否在等待某个事件,并将其从相应的列中删除 */
		if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ){
			( void ) uxListRemove( &( pxTCB->xEventListItem ) );
		}
		else{
			mtCOVERAGE_TEST_MARKER();
		}
			
		uxTaskNumber++;
		/* 要删除的是当前正在运行的任务 */
		if( pxTCB == pxCurrentTCB ){
			/* 把任务添加到等待删除的任务列表中,并在空闲任务中删除 */
			vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
			/* 记录有多少个任务需要释放内存 */
			++uxDeletedTasksWaitingCleanUp;
			/* 任务删除钩子函数---需要用户自己实现*/
			portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
		}
		else{
			/* 要删除的是别的任务 */
			--uxCurrentNumberOfTasks;
			prvDeleteTCB( pxTCB );
			/* 重新计算还要多长时间执行下一个任务 */
			prvResetNextTaskUnblockTime();
		}
		traceTASK_DELETE( pxTCB );
	}
	/* 退出临界段 */
	taskEXIT_CRITICAL();

	/* 判断调度器是否开启 */
	if( xSchedulerRunning != pdFALSE ){
		/* 如果是删除任务本身,马上进行任务调度(释放CPU的使用权)*/
		if( pxTCB == pxCurrentTCB ){
			configASSERT( uxSchedulerSuspended == 0 );
			portYIELD_WITHIN_API();
		}
		else{
			mtCOVERAGE_TEST_MARKER();
		}
	}
}

3. 任务创建和删除函数实例

本实验设计三个任务:

start_task:用来创建其他两个任务

task1_task:此任务运行5次后调用vTaskDelete函数删除任务task2_task,同时控制LED0的闪烁

task2_task:此任务控制控制LED1的闪烁

任务设置

#define START_TASK_PRIO 1 //任务优先级 
#define START_STK_SIZE 128 //任务堆栈大小 
TaskHandle_t StartTask_Handler; //任务句柄 
void start_task(void *pvParameters); //任务函数 

#define TASK1_TASK_PRIO 2 //任务优先级
#define TASK1_STK_SIZE 128 //任务堆栈大小
TaskHandle_t Task1Task_Handler; //任务句柄
void task1_task(void *pvParameters); //任务函数

#define TASK2_TASK_PRIO 3 //任务优先级
#define TASK2_STK_SIZE 128 //任务堆栈大小
TaskHandle_t Task2Task_Handler; //任务句柄
void task2_task(void *pvParameters); //任务函数

main()函数

int main(void){
	......
	//创建开始任务
	xTaskCreate((TaskFunction_t )start_task, 	//任务函数 
				(const char* )"start_task", 	//任务名称
				(uint16_t )START_STK_SIZE, 		//任务堆栈大小
				(void* )NULL, 					//传递给任务函数的参数
				(UBaseType_t )START_TASK_PRIO, 	//任务优先级
				(TaskHandle_t* )&StartTask_Handler); //任务句柄
	vTaskStartScheduler(); 						//开启任务调度
}

任务函数

/* 开始任务任务函数 */
void start_task(void *pvParameters){
	taskENTER_CRITICAL(); //进入临界区
	//创建 TASK1 任务
	xTaskCreate((TaskFunction_t )task1_task,
				(const char* )"task1_task",
				(uint16_t )TASK1_STK_SIZE,
				(void* )NULL,
				(UBaseType_t )TASK1_TASK_PRIO,
				(TaskHandle_t* )&Task1Task_Handler);
	//创建 TASK2 任务
	xTaskCreate((TaskFunction_t )task2_task,
				(const char* )"task2_task",
				(uint16_t )TASK2_STK_SIZE,
				(void* )NULL,
				(UBaseType_t )TASK2_TASK_PRIO,
				(TaskHandle_t* )&Task2Task_Handler);
	vTaskDelete(StartTask_Handler); //删除开始任务
	taskEXIT_CRITICAL(); //退出临界区
}
//task1 任务函数
void task1_task(void *pvParameters){
	uint8_t task1_num=0;
	while(1){
		task1_num++; //任务1执行次数加1
		LED0=!LED0;
		printf("任务1已经执行: %d 次\r\n",task1_num);
		if(task1_num==5){
			vTaskDelete(Task2Task_Handler);//任务 1 执行 5 次删除任务 2 (4)
			printf("任务 1 删除了任务 2!\r\n");
		}
	vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
	}
}
//task2 任务函数
void task2_task(void *pvParameters){
	uint8_t task2_num=0;
	while(1){
		task2_num++; //任务2执行次数加1
		LED1=!LED1;
		printf("任务 2 已经执行: %d 次\r\n",task2_num);
		vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
	}
}

编译程序并下载到开发板中,打开串口助手,显示如下图示:一开始任务1和任务2是同时运行的,由于任务2的优先级比任务1高,所以任务2先输出信息;任务1运行了5次以后任务1就删除了任务2,最后只剩下任务1在运行


您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码: 验证码,看不清楚?请点击刷新验证码 必填



270 次浏览
4次