【FreeRTOS】创建任务、删除任务

目录

  1. 创建任务原型xTaskCreate()函数
  2. 创建任务原型xTaskCreateStatic()函数
  3. 任务控制块
  4. 动态创建任务
  5. 静态创建任务
  6. 删除动态创建任务
  7. 删除静态创建任务
  8. 同一个函数创建多个不同的任务

1.创建任务原型xTaskCreate()函数

位于task.c文件中,如果任务使用xTaskCreate()创建的,则需要从FreeRTOS的堆中自动分配RAM。

BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
							const char * const pcName,  
							const uint16_t usStackDepth,
							void * const pvParameters,  
							UBaseType_t uxPriority,    
							TaskHandle_t * const pxCreatedTask )  
参数1为一个指针,指向实现任务的函数(实际上就是函数名)
参数2为函数的名称,没什么实际含义,就是容易区分是哪个任务
参数3为栈大小,也就是这个任务需要占多大的栈内存,默认最小栈大小为configMINIMAL_STACK_SIZE,每个任务都有它单独的栈内存
参数4为任务的实参,创建任务时,给任务传来参数,void *为万能指针
参数5为任务的优先级设置,数字越小,优先级越低
参数6为创建的任务句柄,操作这个句柄就相当于操作这个任务,可以通过句柄来改变任务的优先级、删除任务等;如果不需要时直接设置为NULL

2.创建任务原型xTaskCreateStatic()函数

位于task.c文件中,如果使用xTaskCreateStatic()创建任务,则需要应用程序编写器提供RAM,这样相对于xTaskCreate()来说,需要两个额外的函数参数,但允许在编译时静态分布RAM。

//创建静态任务原型
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为一个指针,指向实现任务的函数(实际上就是函数名)
参数2为函数的名称,没什么实际含义,就是容易区分是哪个任务
参数3为栈大小,也就是这个任务需要占多大的栈内存,默认最小栈大小为configMINIMAL_STACK_SIZE,每个任务都有它单独的栈内存
参数4为任务的实参,创建任务时,给任务传来参数,void *为万能指针
参数5为任务的优先级设置,数字越小,优先级越低
参数6为变量数组,这个数组将被用作已创建任务的堆栈
参数7将用于保存创建的任务的数据结构(TCB)

3.任务控制块

在task.c文件内

//TCB_t 结构体,就相当于handle句柄
typedef struct tskTaskControlBlock
{
	volatile StackType_t	*pxTopOfStack;	

	#if ( portUSING_MPU_WRAPPERS == 1 )
		xMPU_SETTINGS	xMPUSettings;		
	#endif
	
	ListItem_t			xStateListItem;	
	ListItem_t			xEventListItem;		
	UBaseType_t			uxPriority;			
	StackType_t			*pxStack;			
	char				pcTaskName[ configMAX_TASK_NAME_LEN ];

	#if ( portSTACK_GROWTH > 0 )
		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; 		
	#endif

	#if( INCLUDE_xTaskAbortDelay == 1 )
		uint8_t ucDelayAborted;
	#endif

} tskTCB;

typedef tskTCB TCB_t;

4.动态创建任务

新创建的任务最初都处于就绪状态,如果没有更高优先级的任务可以运行,则会立即成为正在运行的状态任务。创建任务可以在启动调度程序之前和之后。
动态创建任务也就相当于是在堆内创建任务,FreeRTOS 做法是在 SRAM 里面定义一个大数组,也就是堆内存,供 FreeRTOS 的动态内存分配函数使用。

//FreeRTOSConfig.h
#define configTOTAL_HEAP_SIZE		( ( size_t ) ( 17 * 1024 ) )//系统所有总的堆内存的大小
//main.c文件
//写一个任务1函数,让其打印1,也就对应于上文的参数1
void Task1Function(void *param)
{
	while(1)
	{
		printf(1);
	}
}

//写一个任务2函数,让其打印2,也就是也就是对应月上文的参数1
void Task2Function(void *param)
{
	while(1)
	{
		printf("2"):
	}
}

int main(void)
{
	//创建一个任务句柄,也就是对应于上文的参数6
	TaskHandle_t xHandleTask1;
	
	printf("Hello World!");
   //动态创建Task1
	xTaskCreate(Task1Function, //创建一个任务
				"Task1",//任务名
				100,//栈内存
				NULL,//实参
				1,//优先级
				&xHandleTask1//句柄
				);
	//动态创建Task2
	xTaskCreate(Task2Function,
				"Task2",
				100,
				NULL,
				1,//与Task1一样,设置优先级为1
				NULL//未使用句柄
				}
	vTaskStartScheduler();          //开启任务调度
}

此处Task1和Task2都使用串口1打印,两个任务交替执行,程序debug的结果为:在这里插入图片描述
如果Task1和Task2使用两个串口来打印数据,则看起来像是两个任务同时进行,但实际上是时间片流转的方式,将一个任务拆分为一段一段的小任务,然后两个任务交替执行。

ps:当然这些任务也可以改为led=0延时1s,led=1延时1s来实现灯的闪烁,这也算是实现了嵌入式实时操作系统的点灯。值得注意的事情是,如果要使用延时函数必须得使用FreeRTOS的延时函数vTaskDelay(),不能使用裸机程序的延时函数。FreeRTOS提供的延时函数是阻塞延时,当前任务会被挂起,调度器执行当前的就绪任务,从而实现多任务。而裸机程序提供的延时函数则整个任务会形成死循环,如果当前任务恰好的优先级最高,则只能执行此任务,其余任务无法执行,无法实现多任务。
FreeRTOS的延时函数最小为ms级,如果想要单位为us的话,则需要使用使用SysTick.c 文件中的延时函数,注意此文件的两个延时函数与裸机的延时函数不同。

5. 静态创建任务

实现静态任务的创建,首先需要先将configSUPPORT_STATIC_ALLOCATION参数设置为1,其次需要设置两个函数vApplicationGetIdleTaskMemory()这个函数是用户设定的空闲(Idle)任 务的堆栈大小,必须由用户自己分配,而不能是动态分配

//FreeRTOSConfig.h文件
//使用静态方法来创建任务的时候,首先需要将configSUPPORT_STATIC_ALLOCATION参数设置为1
#define configSUPPORT_STATIC_ALLOCATION 1  //支持静态分配内存

//main.c文件
//写任务1函数
void Task1Function(void *param)
{
	while(1)
	{
		printf("1"):
	}
}
//写任务2函数
void Task2Function(void *param)
{
	while(1)
	{
		printf("2"):
	}
}
//写任务3的函数
void Task3Function(void *param)
{
	while(1)
	{
		printf("3"):
	}
}

//创建任务3的堆栈
StaticTask_t  xTask3Buffer[100];
//创建任务3的任务块
StaticTask_t xTask3TCB;

//创建空闲任务的堆栈
StaticTask_t  xIdleTaskBuffer[100];
//创建空闲任务的任务块
StaticTask_t xIdleTaskTCB;

void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
									StackType_t **ppxIdleTaskStackBuffer,
									uint32_t * pulIdleTaskStackSize)
{
	*ppxIdleTaskTCBBuffer=&xIdelTaskTCB;  //任务控制内存
	*ppxIdleTaskStackBuffer=xIdleTaskStack;//任务堆栈内存
	*pulIdleTaskStackSize=100;//任务堆栈大小
}


int main()
{
	//创建任务1的句柄,也就是对应于上文的参数6
	TaskHandle_t xHandleTask1;
	
	printf("Hello World!");
	
	//动态创建任务1
	xTaskCreate(Task1Function,//函数名
						"task1"//函数名称
						100//堆栈内存
						NULL//函数实参
						1//优先级
						&xHandleTask1
						);
	//动态创建任务2
	xTaskCreate(Task2Function,//函数名
						"task2"//函数名称
						100//堆栈内存
						NULL//函数实参
						1//优先级
						NULL
						);
	//静态创建任务3
	xTaskCreateStatic(Task3Function,//函数名
						"task3"//函数名称
						100//堆栈内存
						NULL//函数实参
						1//优先级
						xTask3Buffer,//变量数组
						&xTask3TCB,				
						);
	vTaskStartScheduler();          //开启任务调度
}

当设置任务3的优先级也为1时,程序执行的结果:
在这里插入图片描述

当设置任务3的优先级为2时,程序执行的结果:
在这里插入图片描述

对FreeRTOS而言,高优先级的任务先执行,除非它主动放弃执行,低优先级的任务才能执行,否则低优先级的任务永远无法执行;同优先级的任务交替执行。

6.删除动态创建任务

删除任务的函数:void vTaskDelete( TaskHandle_t xTaskToDelete );
参数为函数的句柄
动态创建的任务只能删除同为动态创建的任务,比如此处Task2可以删除Task1,也可以自杀

//在上面创建任务的任务2函数中增加语句实现删除任务1和任务2自杀
TaskHandle_t xHandleTask1;//这句话要改为全局变量,放在main函数的外面

void Task2Function(void *param)
{
	int i=0;
	while(1)
	{
		printf("2"):
		if(i++ == 100)
		{
			vTaskDelete( xHandleTask1 );//删除任务1
		}
		if(i == 200)
		{
			vTaskDelete( NULL);//任务2自杀
		}
	}
} 

程序执行的结果为:
在这里插入图片描述

7.删除静态创建任务

删除静态创建任务相对于动态创建任务更麻烦一些,因为静态创建任务的参数并没有TaskHandle_t类型的参数,但是xTaskCreateStatic()函数的返回值为TaskHandle_t类型,因此我们可以定义一个全局变量来存放此函数的返回值。
静态创建的任务只能删除同为静态创建的任务,比如此处Task3自杀

//全局变量
TaskHandle_t xHandleTask3;

//Task3函数
void Task3Function(void *param)
{
	int j=0;
	while(1)
	{
		printf("3");
	}
	if(j++ ==5)
	{
		vTaskDelete( xHandleTask3 );//任务3自杀
	}
}

//main函数将xTaskCreateStatic()函数的返回值赋值给xHandleTask3;
xHandleTask3=xTaskCreateStatic(Task3Function,//函数名
						"task3"//函数名称
						100//堆栈内存
						NULL//函数实参
						1//优先级
						xTask3Buffer,//变量数组
						&xTask3TCB,				
						);

程序运行结果:
在这里插入图片描述

8. 同一个函数创建多个不同的任务

每个任务都占有不同的堆栈,因此同一个函数可以创建多个任务

//创建一个通用的任务函数
void TaskGeneralFunction(void * param)
{
	int val = (void *)param;
	while(1)
	{
		printf("%d",val);
	}
}

//main函数内添加下面两行

xTaskCreate(TaskGeneralFunction,
			"Task4",
			100,
			(void *)4,
			1,
			NULL);
xTaskCreate(TaskGeneralFunction,
			"Task5",
			100,
			(void *)5,
			1,
			NULL);

运行的结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS是一个开源的实时操作系统,它提供了一套任务调度机制,使得用户可以方便地创建删除和管理任务。然而,有时候在删除任务后再次创建相同任务时可能会出现卡死现象。 造成这个问题的原因可以有多种可能,下面我介绍几种常见的原因和解决方案: 1.任务资源未完全释放:任务删除后,任务资源(包括任务堆栈、任务控制块等)需要完全释放才能重新创建任务。如果任务资源未完全释放,再次创建任务时可能会出现卡死现象。可以通过调用FreeRTOS提供的函数来确保任务资源完全释放。 2.内存泄漏:任务删除后,如果有内存泄漏的情况,再次创建任务时可能会导致系统资源不足而卡死。这种情况下可以使用内存泄漏检测工具来定位和修复内存泄漏问题。 3.任务优先级冲突:任务删除后再次创建,可能会出现任务优先级冲突的情况。如果新创建任务优先级与其他任务冲突,可能会导致系统调度混乱而卡死。可以检查任务优先级设置,确保任务的优先级合理。 4.任务删除创建的时机问题:任务删除创建的时机也可能会影响到卡死现象的出现。如果删除任务时机不当,再次创建任务时可能会导致系统状态异常而卡死。可以调整任务删除创建的时机,确保在合适的时间进行任务删除创建操作。 总之,freertos任务删除后再创建卡死问题可能由任务资源未完全释放、内存泄漏、任务优先级冲突和任务删除创建的时机问题等原因引起。可以通过合理的资源管理、内存泄漏检测、任务优先级设置和任务删除创建时机调整等方式来解决这个问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值