FreeRTOS任务创建和删除

1. FreeRTOS任务创建

FreeRTOS有两种方式创建任务,动态方法和静态方法,他们的区别就是动态创建任务所使用到的堆栈由系统自动分配,而静态创建任务所使用到的堆栈则要由程序员自己指定了。

1.1 动态方法

FreeRTOS动态方法创建任务的API是xTaskCreate,其函数原型如下:

/* 动态方法创建任务的API */
BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,				// 任务函数
                        const char * const pcName,				// 任务名称
                        const uint16_t usStackDepth,			// 堆栈大小,实际大小为4 * usStackDepth
                        void * const pvParameters,				// 传递给任务函数的参数
                        UBaseType_t uxPriority,					// 任务优先级
                        TaskHandle_t * const pxCreatedTask 		// 任务句柄(可以理解为就是该任务本身)
                      );

参数介绍

  1. xTaskCreate: 该参数就是指向用户任务函数的指针。任务函数也是一个C函数而已,只不过任务函数没有返回值,函数参数是一个void型指针,任务函数里面通常是一个死循环,里面执行着用户想要实现的任务功能。任务函数原型:void task_function( void *pvParameters )

  2. pcName: 任务名称。这个参数不会被 FreeRTOS 使用,其只是单纯地用于辅助调试。应用程序可以通过定义常量 config_MAX_TASK_NAME_LEN 来定义任务名的最大长度——包括’\0’结束符。如果传入的字符串长度超过了这个最大值,字符串将会自动被截断。

  3. usStackDepth: 我们创建的每个任务,他们都有唯一属于自己的任务堆栈空间。usStackDepth 值用于告诉内核为该任务分配多大的栈空间,单位是字节。实际分配的大小为:4 * usStackDepth 字节。

  4. pvParameters: 传递给任务函数的参数。

  5. uxPriority: 任务优先级,FreeRTOS中任务优先级的取值范围是: 0 ~ (configMAX_PRIORITIES – 1),取值越大说明优先级越高。

    其中,configMAX_PRIORITIES 这个宏是由用户定义的常量,FreeRTOS并没有规定优先级的上限,但是我们在使用的时候,最好定义一个自己实际需要的大小,这样可以避免浪费内存。

  6. pxCreatedTask: 任务句柄。这个句柄将在 API 调用中对该创建出来的任务进行引用, 比如改变任务优先级, 或者删除任务。如果应用程序中不会用到这个任务的句柄,则 pxCreatedTask 可以被设为 NULL。

返回值

该API有两个返回值:

  1. pdTRUE,表明任务创建成功。
  2. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY,表明任务创建失败。一般任务创建失败都是由于内存不足造成的。

1.2 静态方法

静态方法创建任务,需要用户自己指定任务堆栈和任务控制块,静态方法创建任务会比动态方法更复杂一些。静态方法创建任务函数原型如下:

/* 静态方法创建任务的API,创建成功函数返回该任务句柄 */
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 	// 任务控制块
                              );

参数介绍(只介绍和动态方法不同的几个参数)

  1. usStackDepth: 任务堆栈大小,由于本函数是静态方法创建任务,所以任务堆栈由用户给出(一般是个用户定义一个数组),所以此参数就是这个数组的大小。
  2. puxStackBuffer: 任务堆栈,一般是用户定义的一个数组,数组类型要为StackType_t 类型。
  3. pxTaskBuffer: 任务控制块。

返回值

  1. NULL: 表明任务创建失败,一般是 puxStackBuffer 或 pxTaskBuffer 用户定义的堆栈大小,任务控制块内存大小不足导致失败。

  2. 创建成功,返回该任务的任务句柄。

2. FreeRTOS任务删除

当我们觉得某个任务已经不需要时,FreeRTOS提供vTaskDelete()函数来删除任务自身或者其他任务,任务一旦被删除之后,相当于不存在了,后续也不可能进入运行态。

如果删除的这个任务,是使用动态方法创建的,那么这个任务删除后,这个任务所用到的堆栈和任务控制块会被空闲任务自动回收。因此有一点很重要,那就是使用 vTaskDelete() 函数的任务千万不能把空闲任务的执行时间饿死,否则空闲任务得不到执行,那么被删除的任务内存也就回收不了了。

另外,该任务的内存只有由内核自动分配的,才会由空闲任务自动回收。而那些由用户自己申请的内存,比如pvPortMalloc()申请的内存,在任务删除后,需要用户自己去释放这部分内存,内核是不会自动帮我们回收的。

删除任务的函数原型如下:

void vTaskDelete( xTaskHandle pxTaskToDelete );

其中参数pxTaskToDelete为要删除的某个任务句柄,另外任务可以传入NULL来删除自身。

3. FreeRTOS任务创建和删除代码示例

我们来设计一个例子。

  1. 首先在main函数里创建优先级为1的任务1,当任务1运行的时候,以优先级2创建任务2。这时因为任务2的优先级最高,所以会马上得到执行。
  2. 任务2执行时,啥都不做,它只是把自己删除了(vTaskDelete传入NULL即可把自身删除)。
  3. 当任务2删除自身之后,任务1又成为了最高的优先级任务,任务1继续执行,这时需要调用vTaskDelay()函数来阻塞一小段时间,让空闲任务得以执行,从而回收被删除了的任务2的内存。
  4. 当任务1退出阻塞态后,再次成为就绪态中具有最高优先级的任务,因此会抢占空闲任务。任务1再次得以运行,如此往复下去。

代码清单如下:

#include <stdio.h>

#include "bsp_usart.h"
#include "FreeRTOS.h"
#include "task.h"

TaskHandle_t StartTask_Handler;			//task1任务句柄

/* task2函数 */
void task2(void *pvParameters)
{	
	for ( ; ; )
	{
		printf("task2 is running and about to delete itself\n");

		vTaskDelete( NULL );		// 任务2把自己删除了
	}
}

/* task1函数 */
void task1(void *pvParameters)
{	
	for ( ; ; )
	{
		printf("task1 is running\n");

		xTaskCreate( task2, "task2", 1000, NULL, 2, NULL );		// 创建任务2

		vTaskDelay(1000);		// 延时1s,让空闲任务得以运行,从而回收被删除的任务2的内存
	}
}

int main()
{
	USART_Config();		// 串口初始化

	/* 创建任务1 */
    xTaskCreate((TaskFunction_t )task1,           	 	 //任务函数
                (const char*    )"task1",          		 //任务名称
                (uint16_t       )128,        			 //任务堆栈大小
                (void*          )NULL,             		 //传递给任务函数的参数
                (UBaseType_t    )1,       				 //任务优先级
                (TaskHandle_t*  )StartTask_Handler);     //任务句柄
				
    vTaskStartScheduler();          					 //开启任务调度
}

代码编译运行后,在串口助手上看到的运行结果如下:
在这里插入图片描述
和我们设计时分析的运行过程时一模一样的。为了更直观的了解这个例子的运行过程,可以看下面的任务运行的流程图:
在这里插入图片描述

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值