4 FreeRTOS的任务创建和删除

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

任务的创建和删除本质就是调用F热额RTOS的API函数

动态创建任务:任务的任务管理控制块以及任务栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配(申请的空间是FreeRTOS管理的)

静态创建任务:任务的任务管理控制块以及任务栈空间所需的内存,需用户分配提供(申请的空间是不归FreeRTOS管理的,独立的)

API函数描述
xTaskCreat()动态方式创建任务
xTaskCreatStatic()静态方式创建任务
vTaskDelete()删除任务

1.1 动态创建任务函数

BaseType_t xTaskCreate
(   TaskFunction_t 				pxTaskCode,		/* 指向任务函数的指针 */				
    const char * const 		    pcName, 	/* 任务名字,最大长度configMAX_TASK_NAME_LEN */
	const 	configSTACK_DEPTH_TYPE 	usStackDepth, 	/* 任务堆栈大小,注意字为单位 */
	void * const 					pvParameters,	/* 传递给任务函数的参数 */
	UBaseType_t 					uxPriority,		/* 任务优先级,范围:0 ~ configMAX_PRIORITIES - 1 */
	TaskHandle_t * const 			pxCreatedTask 	/* 任务句柄,就是任务的任务控制块 */
)
返回值描述
pdPASS任务创建成功
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY任务创建失败

实现动态创建任务流程:

1)将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1

2)定义函数入口参数

3)编写任务函数

使用只需要这三步,此函数创建的任务会立刻进入就绪状态,由任务调度器运行。

动态创建任务函数内部实现:

1)申请堆栈内存&任务控制块内存

2)TCB任务控制块结构体成员赋值

3)添加新任务到就绪列表中

1.2 任务控制块结构体成员介绍

typedef struct tskTaskControlBlock       
{
    volatile StackType_t 		* pxTopOfStack; 	/* 任务栈栈顶,必须为TCB的第一个成员 */
   	ListItem_t 			        xStateListItem;     /* 任务状态列表项 */      
	ListItem_t 			        xEventListItem;		/* 任务事件列表项 */     
    UBaseType_t 			    uxPriority;         /* 任务优先级,数值越大,优先级越大 */
    StackType_t 			    * pxStack;			/* 任务栈起始地址 */
    char 				        pcTaskName[ configMAX_TASK_NAME_LEN ]; 	/* 任务名字 */		
	…
	省略很多条件编译的成员
} tskTCB;

任务栈栈顶,在任务切换时的任务上下文保存、任务恢复息息相关

注意:每个任务都有属于自己的任务控制块,类似于身份证

1.3 静态创建任务函数

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		/* 任务控制块指针,由用户分配 */
); 	
返回值描述
NULL用户没有提供相应的内存,创建任务失败
其他值任务句柄,任务创建成功

静态创建任务使用流程;

1)将宏configSUPPORT_STATIC_ALLOCATION 配置为 1

2)定义空闲任务&定时器任务的任务堆栈及TCB

3)实现两个接口函数

  • vApplicationGetIdleTaskMemory( ) 空闲任务内存赋值

  • vApplicationGetTimerTaskMemory ( ) 软件定时器内存赋值

4)定义函数入口参数

5)编写任务函数

使用只需要以上五个步骤,此函数创建的任务会立刻进入就绪状态,由任务调度器调度运行。

静态创建内部实现:

1)TCB结构体成员赋值

2)添加新任务到就绪列表中

1.4 任务删除函数

void vTaskDelete(TaskHandle_t xTaskToDelete); //xTaskToDelete指的是待删除任务的任务句柄

任务删除函数用于删除已经被成功创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中删除。

注意;

  • 当传入的参数为NULL,代表删除任务自身(当前正在运行的任务)
  • 空闲任务会负责释放被删除任务中由系统分配的内存(指的是传入参数是NULL的时候,即删除任务本身的时候由空闲任务负责释放内存,当删除任务不是正在运行的任务的时候,是由vTaskDelete()函数释放内存的),但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放,否则将导致内存泄漏。

删除任务流程:

1)使用删除任务函数,将宏INCLUDE_vTaskDelete配置为1

2)入口参数输入需要删除的任务句柄(NULL代表删除本身)

内部实现过程:

1)获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除任务本身。

2)将被删除任务移除所在列表:将该任务在所在列表中移除,包括:阻塞、挂起、就绪、事件等列表。

3)判断所需要删除的任务:

  • 删除任务本身,需先添加到等待删除列表,内存释放将在空闲任务执行
  • 删除其他任务,释放内存,任务数量

4)更新下个任务的阻塞时间:更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务,然后可以及时更新超时时间。

2 任务创建和删除(动态方法)

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define START_TASK_PRIO        1
 #define START_TASK_STACK_SIZE  128            //单位是字,因此需要*4变成字节
 TaskHandle_t        start_task_handler;
 void start_task( void * pvParameters );
 
 /* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define TASK1_PRIO        2
 #define TASK1_STACK_SIZE  128            //单位是字,因此需要*4变成字节
 TaskHandle_t        task1_handler;
 void task1( void * pvParameters );
 
 /* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define TASK2_PRIO        3
 #define TASK2_STACK_SIZE  128            //单位是字,因此需要*4变成字节
 TaskHandle_t        task2_handler;
 void task2( void * pvParameters );
 
 /* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define TASK3_PRIO        4
 #define TASK3_STACK_SIZE  128            //单位是字,因此需要*4变成字节
 TaskHandle_t        task3_handler;
 void task3( void * pvParameters );
/******************************************************************************************************/


/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{
 	xTaskCreate(  (TaskFunction_t         ) start_task,               //指向任务函数的指针
								(char *                 ) "start_task",             //任务名
								(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,    //堆栈大小,宏定义
								(void *                 ) NULL,                     //入口参数,一般不设置
								(UBaseType_t            ) START_TASK_PRIO,          //任务优先级,宏定义
								(TaskHandle_t *         ) &start_task_handler);     //任务句柄,即任务控制块
   
	vTaskStartScheduler();  //开启任务调度器,当创建完任务之后,该函数必须开启要不然任务无法调度
}

//定义开始任务函数
//start_task:用来创建其他三个任务,并且只创建一次,创建完成后需要删除掉start_task
void start_task( void * pvParameters )
{
	//关闭中断
	taskENTER_CRITICAL();      //进入临界区
	
	//创建任务1
	xTaskCreate(  (TaskFunction_t         ) task1,               //指向任务函数的指针
								(char *                 ) "task1",             //任务名
								(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,    //堆栈大小,宏定义
								(void *                 ) NULL,                     //入口参数,一般不设置
								(UBaseType_t            ) TASK1_PRIO,          //任务优先级,宏定义
								(TaskHandle_t *         ) &task1_handler);     //任务句柄,即任务控制块
	
	//创建任务2
	xTaskCreate(  (TaskFunction_t         ) task2,               //指向任务函数的指针
								(char *                 ) "task2",             //任务名
								(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,    //堆栈大小,宏定义
								(void *                 ) NULL,                     //入口参数,一般不设置
								(UBaseType_t            ) TASK2_PRIO,          //任务优先级,宏定义
								(TaskHandle_t *         ) &task2_handler);     //任务句柄,即任务控制块
								
	//创建任务3
	xTaskCreate(  (TaskFunction_t         ) task3,               //指向任务函数的指针
								(char *                 ) "task3",             //任务名
								(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE,    //堆栈大小,宏定义
								(void *                 ) NULL,                     //入口参数,一般不设置
								(UBaseType_t            ) TASK3_PRIO,          //任务优先级,宏定义
								(TaskHandle_t *         ) &task3_handler);     //任务句柄,即任务控制块
								
	//创建完三个任务,需要删掉start_task()函数
	vTaskDelete(NULL);		
		
	taskEXTI_CRITICAL();     //退出临界区
								
}

//定义task1的任务函数
//task1:用来实现LED0每500ms翻转一次
void task1( void * pvParameters )
{
	while(1)
	{
		printf("task1正在运行!!!\r\n");
		LED0_TOGGLE();   //LED0翻转函数
		vTaskDelay(500); //系统自带的延时函数
	}
}

//定义task2的任务函数
//task2:用来实现LED1每500ms翻转一次
void task2( void * pvParameters )
{
	while(1)
	{
		printf("task2正在运行!!!\r\n");
		LED1_TOGGLE();   //LED1翻转函数
		vTaskDelay(500); //系统自带的延时函数

	}
}

//定义task3的任务函数
//task3:用来判断按键KEY0,按下KEYO删除task1
void task3( void * pvParameters )
{
	uint8_t key = 0; 
	while(1)
	{
		printf("task3正在运行!!!\r\n");
		key_scan(0);              //不支持连按
		if(key == KEY0_PRES)
		{
			if(task1_handler != NULL)    //避免重复删除带来的错误
			{
				printf("删除task1!!!\r\n");
			  vTaskDelete(task1_handler);
				task1_handler =NULL;
			}
		}
		vTaskDelay(10);
	}
}

如果上面这段程序中没有临界函数:

taskENTER_CRITICAL();      //进入临界区
taskEXTI_CRITICAL();     //退出临界区

则运行程序结果会如下图所示:

原因如下:

首先创建开始任务,开始任务优先级是最低的,开始任务一创建好之后,就开启任务调度,在开始任务中又创建了三个任务,首先创建task1之后,由于task1的优先级比start_task优先级高,因此会先执行task1的任务,然后进入阻塞之后(延时500ms)task1释放CPU使用权给start_task继续执行,这时候开始创建task2,由于task2优先级也比start_task优先级高,因此会执行task2的任务,然后进入阻塞之后(延时500ms)task2释放CPU使用权给start_task继续执行,这时候开始创建task3,由于task3优先级也比start_task优先级高,因此会执行task3的任务,然后进入阻塞之后(延时10ms)task3释放CPU使用权给start_task继续执行,vTaskDelte()函数。

如果需要一开始高优先级先执行,可以刚进入start_task()的时候关闭任务调度器。任务切换其实是在中断中执行的,FreeRTOS提供了API函数:进入临界区,就是用来关闭中断。进入临界区函数底层其实操作的是basepri寄存器。

因此在start_task中进入临界区,然后等创建完三个任务,退出临界区,才会使得优先级高的先运行,即运行task3。

3 任务创建和删除(静态方法)

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define START_TASK_PRIO        1
 #define START_TASK_STACK_SIZE  128            //单位是字,因此需要*4变成字节
 TaskHandle_t        start_task_handler;
 StackType_t         start_task_stack[START_TASK_STACK_SIZE];
 StaticTask_t        start_task_tcb;
 void start_task( void * pvParameters );
 
 /* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define TASK1_PRIO        2
 #define TASK1_STACK_SIZE  128            //单位是字,因此需要*4变成字节
 TaskHandle_t        task1_handler;
 StackType_t         task1_stack[TASK1_STACK_SIZE];
 StaticTask_t        task1_tcb;
 void task1( void * pvParameters );
 
 /* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define TASK2_PRIO        3
 #define TASK2_STACK_SIZE  128            //单位是字,因此需要*4变成字节
 TaskHandle_t        task2_handler;
 StackType_t         task2_stack[TASK2_STACK_SIZE];
 StaticTask_t        task2_tcb;
 void task2( void * pvParameters );
 
 /* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define TASK3_PRIO        4
 #define TASK3_STACK_SIZE  128            //单位是字,因此需要*4变成字节
 TaskHandle_t        task3_handler;
 StackType_t         task3_stack[TASK3_STACK_SIZE];
 StaticTask_t        task3_tcb; 
 void task3( void * pvParameters );
 
 
 
//空闲任务配置
StaticTask_t idle_task_tcb;
StackType_t  idle_task_stack[configMINIMAL_STACK_SIZE];

//软件定时器任务配置
StaticTask_t timer_task_tcb;
StackType_t  timer_task_stack[configTIMER_TASK_STACK_DEPTH];

/******************************************************************************************************/



//空闲任务内存分配
//任务控制块内存、任务堆栈内存、任务堆栈的大小
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, //空闲任务任务控制块,用来保存任务的一些特征信息
                                    StackType_t ** ppxIdleTaskStackBuffer, //空闲任务任务堆栈
                                    uint32_t * pulIdleTaskStackSize ) 
{
	* ppxIdleTaskTCBBuffer   = &idle_task_tcb;
	* ppxIdleTaskStackBuffer = idle_task_stack;  //自己分配一个数组,或者通过malloc函数申请
  * pulIdleTaskStackSize	 = configMINIMAL_STACK_SIZE;
}


//软件定时器内存分配
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
                                     StackType_t ** ppxTimerTaskStackBuffer,
                                     uint32_t * pulTimerTaskStackSize )
{
	* ppxTimerTaskTCBBuffer   = &timer_task_tcb;
	* ppxTimerTaskStackBuffer = timer_task_stack;
	* pulTimerTaskStackSize   = configTIMER_TASK_STACK_DEPTH;
}

/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{
 	start_task_handler = xTaskCreateStatic(   (TaskFunction_t) 		start_task,
																						(char *        ) 		"start_task", 
																						(uint32_t      ) 		START_TASK_STACK_SIZE,
																						(void *        ) 		NULL,
																						(UBaseType_t   ) 		START_TASK_PRIO,
																						(StackType_t * ) 		start_task_stack,
																						(StaticTask_t *) 		&start_task_tcb ); 

	vTaskStartScheduler();  //开启任务调度器,当创建完任务之后,该函数必须开启要不然任务无法调度
}

//定义开始任务函数
//start_task:用来创建其他三个任务,并且只创建一次,创建完成后需要删除掉start_task
void start_task( void * pvParameters )
{
	//关闭中断
	taskENTER_CRITICAL();      //进入临界区
	
	//创建task1
 	task1_handler = xTaskCreateStatic(  (TaskFunction_t) 		task1,
																			(char *        ) 		"task1", 
																			(uint32_t      ) 		TASK1_STACK_SIZE,
																			(void *        ) 		NULL,
																			(UBaseType_t   ) 		TASK1_PRIO,
																			(StackType_t * ) 		task1_stack,
																			(StaticTask_t *) 		&task1_tcb ); 	
	
	//创建task2
  task2_handler = xTaskCreateStatic(  (TaskFunction_t) 		task2,
																			(char *        ) 		"task2", 
																			(uint32_t      ) 		TASK2_STACK_SIZE,
																			(void *        ) 		NULL,
																			(UBaseType_t   ) 		TASK2_PRIO,
																			(StackType_t * ) 		task2_stack,
																			(StaticTask_t *) 		&task2_tcb );	
	
	//创建task3
  task3_handler = xTaskCreateStatic(  (TaskFunction_t) 		task3,
																			(char *        ) 		"task3", 
																			(uint32_t      ) 		TASK3_STACK_SIZE,
																			(void *        ) 		NULL,
																			(UBaseType_t   ) 		TASK3_PRIO,
																			(StackType_t * ) 		task3_stack,
																			(StaticTask_t *) 		&task3_tcb );
											
	
	//创建完三个任务,需要删掉start_task()函数
	vTaskDelete(start_task_handler);		
		
	taskEXTI_CRITICAL();     //退出临界区
								
}

//定义task1的任务函数
//task1:用来实现LED0每500ms翻转一次
void task1( void * pvParameters )
{
	while(1)
	{
		printf("task1正在运行!!!\r\n");
		LED0_TOGGLE();   //LED0翻转函数
		vTaskDelay(500); //系统自带的延时函数
	}
}

//定义task2的任务函数
//task2:用来实现LED1每500ms翻转一次
void task2( void * pvParameters )
{
	while(1)
	{
		printf("task2正在运行!!!\r\n");
		LED1_TOGGLE();   //LED1翻转函数
		vTaskDelay(500); //系统自带的延时函数

	}
}

//定义task3的任务函数
//task3:用来判断按键KEY0,按下KEYO删除task1
void task3( void * pvParameters )
{
	uint8_t key = 0; 
	while(1)
	{
		printf("task3正在运行!!!\r\n");
		key_scan(0);              //不支持连按
		if(key == KEY0_PRES)
		{
			if(task1_handler != NULL)    //避免重复删除带来的错误
			{
				printf("删除task1!!!\r\n");
			  vTaskDelete(task1_handler);
				task1_handler =NULL;
			}
		}
		vTaskDelay(10);
	}
}

4 总结

FreeRTOS是一个用于嵌入式系统的实时操作系统。在FreeRTOS中,任务是系统中最基本的执行单位。下面是关于FreeRTOS任务创建和使用的一些基本信息: 1. 在FreeRTOS创建任务的步骤如下: - 使用xTaskCreate()函数创建任务,指定任务函数、任务名称、堆栈大小和优先级等参数。 - 在任务函数中定义任务的行为和逻辑。 - 使用vTaskStartScheduler()函数启动调度器,开始任务调度。 2. 任务函数的定义: - 任务函数是一个无返回值、无参数的函数,它将在任务创建后立即执行。 - 任务函数应该包含一个无限循环,以便任务能够不断地执行。 3. 任务的优先级: - FreeRTOS使用优先级来确定任务的执行顺序。具有较高优先级的任务将在具有较低优先级的任务之前执行。 - 可以使用宏定义configMAX_PRIORITIES来定义系统支持的最大优先级数。 4. 任务挂起和恢复: - 可以使用vTaskSuspend()函数将任务挂起,使其暂停执行。 - 可以使用vTaskResume()函数恢复被挂起的任务,使其继续执行。 5. 任务删除: - 可以使用vTaskDelete()函数删除一个已经创建任务。 这些是FreeRTOS任务创建和使用的基本知识。在实际应用中,还可以使用其他FreeRTOS提供的功能和API来管理和控制任务的执行。请注意,具体的实现细节可能会因为不同的系统和编译器而有所差异,建议参考FreeRTOS官方文档和示例代码进行更详细的学习和了解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值