5、FreeRTOS任务的创建和删除

一、任务函数

函数 描述
xTaskCreate() 使用动态的方法创建一个任务。
xTaskCreateStatic() 使用静态的方法创建一个任务。
xTaskCreateRestricted() 创建一个使用 MPU 进行限制的任务,相关内存使用动态内存分配。
vTaskDelete() 删除一个任务。

二、创建任务和删除任务

2.1 动态创建任务xTaskCreate()

2.1.1 动态创建任务时使用的函数如下:

BaseType_t xTaskCreate( TaskFunction_t 					pxTaskCode, 		// 函数指针, 任务函数
						const char * const 				pcName, 			// 任务的名字
						const configSTACK_DEPTH_TYPE 	usStackDepth, 		// 栈大小,单位为word,10表示40字节
						void * const 					pvParameters, 		// 调用任务函数时传入的参数
						UBaseType_t 					uxPriority,		 	// 优先级
						TaskHandle_t * const 			pxCreatedTask );	// 任务句柄, 以后使用它来操作这个任务		

2.1.2 参数说明:

参数 描述
pxTaskCode 函数指针,可以简单地认为任务就是一个C函数。
它稍微特殊一点:永远不退出,或者退出时要调用"vTaskDelete(NULL)"
pcName 任务的名字,FreeRTOS内部不使用它,仅仅起调试作用。
长度为:configMAX_TASK_NAME_LEN
usStackDepth 每个任务都有自己的栈,这里指定栈大小。
单位是word,比如传入100,表示栈大小为100 word,也就是400字节。
最大值为uint16_t的最大值。
怎么确定栈的大小,并不容易,很多时候是估计。
精确的办法是看反汇编码。
pvParameters 调用pvTaskCode函数指针时用到:pvTaskCode(pvParameters) 传递给任务函数的参数。
uxPriority 优先级范围:0~(configMAX_PRIORITIES – 1)
数值越小优先级越低,
如果传入过大的值,xTaskCreate会把它调整为(configMAX_PRIORITIES – 1)
pxCreatedTask 用来保存xTaskCreate的输出结果:task handle。
以后如果想操作这个任务,比如修改它的优先级,就需要这个handle。
如果不想使用该handle,可以传入NULL
返回值 成功:pdPASS;
失败:errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(失败原因只有内存
不足)
注意:文档里都说失败时返回值是pdFAIL,这不对。
pdFAIL是0,errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY是-1。

2.2 静态创建任务xTaskCreateStatic()

2.2.1 静态创建任务使用函数如下

TaskHandle_t xTaskCreateStatic( TaskFunction_t 			pxTaskCode,
                                 const char * const 	pcName, 
                                 const uint32_t 		ulStackDepth,
                                 void * const 			pvParameters,
                                 UBaseType_t		 	uxPriority,
                                 StackType_t * const 	puxStackBuffer,
                                 StaticTask_t * const 	pxTaskBuffer )

2.2.2 参数说明:

参数 描述
pxTaskCode 函数指针,可以简单地认为任务就是一个C函数。
它稍微特殊一点:永远不退出,或者退出时要调用"vTaskDelete(NULL)"
pcName 任务的名字,FreeRTOS内部不使用它,仅仅起调试作用。
长度为:configMAX_TASK_NAME_LEN
ulStackDepth 任务堆栈大小, 由于本函数是静态方法创建所以用户给出任务堆栈大小,
一般是一个数组,此参数就是这个数组的大小
pvParameters 调用pvTaskCode函数指针时用到:pvTaskCode(pvParameters) 传递给任务函数的参数。
uxPriority 优先级范围:0~(configMAX_PRIORITIES – 1)
数值越小优先级越低,
如果传入过大的值,xTaskCreate会把它调整为(configMAX_PRIORITIES – 1)
puxStackBuffer 任务堆栈,一般为 数组类型要StackType_t类
pxTaskBuffer 任务控制块
返回值 NULL: 任务创建失败, puxStackBuffer或 pxTaskBuffer为 NULL的时候会导致这个 的时候会导致这个 错误的发生。
其他值 : 任

2.3 创建一个使用mpu进行限制的任务xTaskCreateRestricted()

2.3.1 任务使用函数如下(这个函数只是了解,不会使用)

此函数也是用来创建任务的, 只不过此函数要求 所使用的 MCU有 MPU(内存保护单元 内存保护单元 ), 用此函数创建的任务会受到 MPU的保护。 其他的功能和函数 xTaxkCreate()一

BaseType_t xTaskCreateRestricted( const TaskParameters_t * const 	pxTaskDefinition, 
                                 TaskHandle_t * 					pxCreatedTask )

2.3.2 参数说明:

参数 描述
pxTaskDefinition: 指向一个结构体 TaskParameters_t,这个结构体描述了任务的函数、 ,这个结构体描述了任务的函数、 堆栈大小、优先级等。此结构体在文件 task.h中有定义。
pxCreatedTask: 任务句柄
返回值 pdPASS:创建成功
其他值 : 任务未创建成功, 很有可能是因为 FreeRTOS的堆太小

2.4 删除任务vTaskDelete()

2.4.1 任务使用函数如下

vTaskDelete( TaskHandle_t xTaskToDelete )

2.4.2 参数说明:

参数 描述
xTaskToDelete: 要删除的任务句柄
返回值 NULL

三、 实例

实例1:动态创建任务

使用2个函数分别创建2个任务。
任务1的代码:

void vTask1( void *pvParameters )
{
    
    
	const char *pcTaskName = "T1 run\r\n";
	volatile uint32_t ul; /* volatile用来避免被优化掉 */
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
    
    
		/* 打印任务1的信息 */
		printf( "%s ",pcTaskName );
		
		/* 延迟一会(比较简单粗暴) */
			delay_xms(5);
	}
}

任务2的代码:

void vTask2( void *pvParameters )
{
    
    
	const char *pcTaskName = "T2 run\r\n";
	volatile uint32_t ul; /* volatile用来避免被优化掉 */
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
    
    
		/* 打印任务1的信息 */
			printf( "%s ",pcTaskName );
		
		/* 延迟一会(比较简单粗暴) */
		delay_xms(5);
	}
}

main 的代码

int main(void)
{
    
    
	HAL_Init();                    	//初始化HAL库    
	Stm32_Clock_Init(336,8,2,7);  	//设置时钟,168Mhz
	delay_init(168);               	//初始化延时函数
	uart_init(115200);              //初始化USART
		
	xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
	xTaskCreate(vTask2, "Task 2", 1000, NULL, 1, NULL);
		/* 启动调度器 */
	vTaskStartScheduler();
    while(1)
    {
    
    
				printf("Hello is ok\r\n");
			HAL_Delay(1000);
     
    }
}

运行结果如下:

image-20220805152048093

注意:

  • task1 先运行
  • 之前的版本是后面创建的任务先运行,应该是FreeRTOS10版本更新了
  • 更高优先级先运行

实例2:静态创建任务

  • 系统设置

使用静态方法创建任务的时候需要将宏 configSUPPORT_STATIC_ALLOCATION设置为 1, 在文件 FreeRTOSConfig.h中

#define configSUPPORT_STATIC_ALLOCATION 1 //静态内存

宏 configSUPPORT_STATIC_ALLOCATION定义为 1以后编译一次,会提示我们有两个函 以后编译一次,会提示我们有两个函 数未定义,如图所示

扫描二维码关注公众号,回复: 14713334 查看本文章

image-20220805165109249

如果使用静态方法 的 话 需 要 用 户 实 现 两 个 函 数 vApplicationGetIdleTaskMemory() 和vApplicationGetTimerTaskMemory()。通过这两个函数来给空闲任务和定时器服务任务的任务堆栈和任务控制块分配内存,这两个函数我们在 mainc.c 中定义,定义如下:

//空闲任务任务堆栈
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];
//空闲任务控制块
static StaticTask_t IdleTaskTCB;

//定时器服务任务堆栈
static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH];
//定时器服务任务控制块
static StaticTask_t TimerTaskTCB;
//获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的
//静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该
//有用户来提供,FreeRTOS提供了接口函数vApplicationGetIdleTaskMemory()
//实现此函数即可。
//ppxIdleTaskTCBBuffer:任务控制块内存
//ppxIdleTaskStackBuffer:任务堆栈内存
//pulIdleTaskStackSize:任务堆栈大小
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, 
								   StackType_t **ppxIdleTaskStackBuffer, 
								   uint32_t *pulIdleTaskStackSize)
{
    
    
	*ppxIdleTaskTCBBuffer=&IdleTaskTCB;
	*ppxIdleTaskStackBuffer=IdleTaskStack;
	*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;
}

//获取定时器服务任务的任务堆栈和任务控制块内存
//ppxTimerTaskTCBBuffer:任务控制块内存
//ppxTimerTaskStackBuffer:任务堆栈内存
//pulTimerTaskStackSize:任务堆栈大小
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, 
									StackType_t **ppxTimerTaskStackBuffer, 
									uint32_t *pulTimerTaskStackSize)
{
    
    
	*ppxTimerTaskTCBBuffer=&TimerTaskTCB;
	*ppxTimerTaskStackBuffer=TimerTaskStack;
	*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;
}

可以看出这两个函数很简单,用户定义静态的任务堆栈和任务控制块内存,然后将这些内存传递给函数参数。最后创建空闲任务和定时器服务任务的 API 函数会调用vApplicationGetIdleTaskMemory()和 vApplicationGetTimerTaskMemory()来获取这些内存。

vtask3代码

void vTask3( void *pvParameters )
{
    
    
	const char *pcTaskName = "T3 run\r\n";
	volatile uint32_t ul; /* volatile用来避免被优化掉 */
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
    
    
		/* 打印任务1的信息 */
		printf( "%s", pcTaskName);
		
		/* 延迟一会(比较简单粗暴) */
		delay_ms(1000);    
	}
}

main 代码

StackType_t vtask3Stack[100];
StaticTask_t xtask3TCB;
int main(void)
{
    
    
	HAL_Init();                    	//初始化HAL库    
	Stm32_Clock_Init(336,8,2,7);  	//设置时钟,168Mhz
	delay_init(168);               	//初始化延时函数
	uart_init(115200);              //初始化USART
		
//	xTaskCreate(vTaskFunction, "Task 1", 1000, (void *)pcTextForTask1, 1, NULL);
//	xTaskCreate(vTaskFunction, "Task 2", 1000, (void *)pcTextForTask2, 1, NULL);
	
	//xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
		/* 启动调度器 */
	xTaskCreateStatic(vTask3, "Task 3", 100, NULL, 1, vtask3Stack,&xtask3TCB);
	vTaskStartScheduler();
    while(1)
    {
    
    
				printf("Hello is ok\r\n");
			HAL_Delay(1000);
     
    }
}

运行结果

image-20220805164148520

可以看出来是和动态创建任务是一样的,并且这个方式很麻烦,一般情况都使用动态创建!!

实例3:使用任务参数

多个任务可以使用同一个函数,怎么体现它们的差别?

  • 栈不同
  • 创建任务时可以传入不同的参数

我们创建2个任务,使用同一个函数,代码如下:

void vTaskFunction( void *pvParameters )
{
    
    
	const char *pcTaskText = pvParameters;
	volatile uint32_t ul; /* volatile用来避免被优化掉 */
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
    
    
		/* 打印任务的信息 */
		printf( "%s", pcTaskText);
		
		/* 延迟一会(比较简单粗暴) */
		delay_ms(1000);
	}
}

上述代码中的pcTaskText 来自参数pvParameters , pvParameters 来自哪里?创建任务时传入的。

代码如下:

  • 使用xTaskCreate创建2个任务时,第4个参数就是pvParameters
  • 不同的任务,pvParameters不一样
static const char *pcTextForTask1 = "T1 run\r\n";
static const char *pcTextForTask2 = "T2 run\r\n";
int main(void)
{
    
    
	HAL_Init();                    	//初始化HAL库    
	Stm32_Clock_Init(336,8,2,7);  	//设置时钟,168Mhz
	delay_init(168);               	//初始化延时函数
	uart_init(115200);              //初始化USART
		
	xTaskCreate(vTaskFunction, "Task 1", 1000, (void *)pcTextForTask1, 1, NULL);
	xTaskCreate(vTaskFunction, "Task 2", 1000, (void *)pcTextForTask2, 1, NULL);
		/* 启动调度器 */
	vTaskStartScheduler();
    while(1)
    {
    
    
				printf("Hello is ok\r\n");
			HAL_Delay(1000);
     
    }
}

运行结果:

image-20220805152959063

看着和刚刚一样

实例4: 删除任务

本节代码会涉及优先级的知识,可以只看vTaskDelete的用法,忽略优先级的讲解。

我们要做这些事情:

  • 创建任务1:任务1的大循环里,创建任务2,然后休眠一段时间

  • 任务2:打印一句话,然后就删除自己

任务1的代码如下:

TaskHandle_t xTask2Handle = NULL;
void vTask1( void *pvParameters )
{
    
    
	const TickType_t xDelay100ms = pdMS_TO_TICKS( 100UL );		
	BaseType_t ret;
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
    
    
		/* 打印任务的信息 */
		printf("Task1 is running\r\n");
		
		ret = xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle );
		if (ret != pdPASS)
			printf("Create Task2 Failed\r\n");
		
		// 如果不休眠的话, Idle任务无法得到执行
		// Idel任务会清理任务2使用的内存
		// 如果不休眠则Idle任务无法执行, 最后内存耗尽
		vTaskDelay( xDelay100ms );
	}
}

任务2的代码如下:

void vTask2( void *pvParameters )
{
    
    	
	/* 打印任务的信息 */
	printf("Task2 is running and about to delete itself\r\n");

	// 可以直接传入参数NULL, 这里只是为了演示函数用法
	vTaskDelete(xTask2Handle);
}

main函数代码如下:

int main(void)
{
    
    
	HAL_Init();                    	//初始化HAL库    
	Stm32_Clock_Init(336,8,2,7);  	//设置时钟,168Mhz
	delay_init(168);               	//初始化延时函数
	uart_init(115200);              //初始化USART
		
//	xTaskCreate(vTaskFunction, "Task 1", 1000, (void *)pcTextForTask1, 1, NULL);
//	xTaskCreate(vTaskFunction, "Task 2", 1000, (void *)pcTextForTask2, 1, NULL);
	xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
		/* 启动调度器 */
	vTaskStartScheduler();
    while(1)
    {
    
    
				printf("Hello is ok\r\n");
			HAL_Delay(1000);
     
    }
}

运行结果如下:

image-20220805154441562

任务运行图:

  • main函数中创建任务1,优先级为1。任务1运行时,它创建任务2,任务2的优先级是2。
  • 任务2的优先级最高,它马上执行。
  • 任务2打印一句话后,就删除了自己。
  • 任务2被删除后,任务1的优先级最高,轮到任务1继续运行,它调用vTaskDelay() 进入Block状
  • 任务1 Block期间,轮到Idle任务执行:它释放任务2的内存(TCB、栈)
  • 时间到后,任务1变为最高优先级的任务继续执行。
  • 如此循环。

四、工程分享

链接:https://pan.baidu.com/s/1IJJu9LHdEAh_i_lHVwNOSg?pwd=6bj7
提取码:6bj7
–来自百度网盘超级会员V4的分享

猜你喜欢

转载自blog.csdn.net/mayuxin1314/article/details/126181021
今日推荐