FreeRTOS-任务操作

FreeRTOS-任务操作

  • 有了前两节系统配置和任务基础了,接下来我们就使用FreeRTOS提供的一些API完成简单的任务操作。

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

在创建静态任务前,我们需要明确以下几点。

  1. FreeRTOS是一个轻量级操作系统,在运行操作系统的时候,我们需要为该操作系统分配一定空间,我们称为系统总堆。
  2. FreeRTOS每个任务都相当于一个函数,所以任务函数也需要有函数名、函数类型、形参。
  3. 由于每个任务都需要有自己独立的堆栈,所以需要从系统总堆中为其分配任务堆栈,堆栈大小由用户来指定。
  4. 每个任务又需要有任务控制块,而任务控制块也需要一定空间,这一部分空间来自于任务堆栈。
  5. 我们在前面提到过,FreeRTOS中有两个特殊的任务,一个是空闲任务,一个是定时器任务。其中空闲任务优先级最低,定时器任务优先级最高,而且一般我们都会让这两个特殊任务单独占用一个优先级。
  6. 所谓静态,其实就是静态管理内存,内存申请由开发者来完成。

  • 经过上面的分析,接下来我们就开始分析如何用FreeRTOS提供的API完成上述任务。
  1. 为了能够使操作系统正常运行我们为操作系统分配一定空间,在FreeRTOSConifg.h文件中找到"configTOTAL_HEAP_SIZE"
    这个宏,它指定了系统总堆栈大小,堆栈的分配由heap.c文件中的函数来操作。这里我们将其大小设置为20*1024,即20k。
  2. 下图是FreeRTOS静态任务创建函数定义。
    在这里插入图片描述
    从上面定义可以看出,首先需要定义configSUPPORT_STATIC_ALLOCATION,所以我们需要在"FreeRTOSConfig.h"文件中将configSUPPORT_STATIC_ALLOCATION定义为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 )
  • TaskHandle_t 是函数的返回值,任务创建成功后,将返回该任务的任务句柄,任务句柄相当于取得了任务的控制权,以后的删除、挂起、解挂等操作都是操作任务句柄实现的。
  • TaskFunction_t pxTaskCode 任务地址,即把创建任务名传进去即可。
  • const char * const pcName 任务标识符,可以随便设置,但一般与任务名相同。
  • const uint32_t ulStackDepth 任务堆栈大小
  • void * const pvParameters 传递给任务函数的参数,一般无,即NULL
  • UBaseType_t uxPriority 任务优先级
  • StackType_t * const puxStackBuffer 为任务分配的堆栈
  • StaticTask_t * const pxTaskBuffer 任务控制块

3.知道了静态任务创建函数各参数意义之后,接下来我们就开始创建任务,首先我们根据task.h文件中官方定义的任务框架如下图所示。
在这里插入图片描述
则我们在这里同样定义三个任务,一个开始任务,两个具体执行任务,开始任务中创建执行任务,因为执行任务只需要创建一次,所以开始任务创建一次后即删除。执行任务分别为控制LED0以0.5s的间隔状态反转执行5次后删除任务2,控制LED1以0.5s状态转换。所以函数分别定义如下:

/*开始任务*/
void start_task(void* pvParameters)
{
	Task2_Task_Handler= xTaskCreateStatic((TaskFunction_t) task1,//任务名
									  (char*				 ) "task1",//任务表示符
									  (uint32_t			 ) TASK1_STACK_SIZE,//堆栈大小
									  (void*				 ) NULL,
									  (UBaseType_t	 ) TASK1_TASK_PRIO,//任务优先级
									  (StackType_t*	 ) task1Stack,//任务堆栈,实际就是一数组
									  (StaticTask_t* ) &task1TaskBuffer); //任务控制块

										
	Task2_Task_Handler = xTaskCreateStatic((TaskFunction_t) task2,
									  (char*				 ) "task2",
									  (uint32_t			 ) TASK2_STACK_SIZE,
									  (void*				 ) NULL,
									  (UBaseType_t	 ) TASK2_TASK_PRIO,
									  (StackType_t*	 ) task2Stack,
									  (StaticTask_t* ) &task2TaskBuffer);
						
   vTaskDelete(Start_Task_Handler); //任务创建只需要一次,所以创建完毕后立刻删除开始任务
   //这里也可以用vTaskDelete(NULL);当不输入参数时默认删除当前任务
}


/*任务1 0.5s为间隔LED闪烁,执行5次后删除task2*/
void task1(void* pvParameters)
{
	u8 t1_num=0;
	while(1)
	{
		t1_num++;
		if (t1_num == 5)
		{
			vTaskDelete(Task2_Task_Handler);//删除任务2
		}
		LED0 = ~LED0;
		vTaskDelay(500);//FreeRTOS延时函数,延时期间会引起任务调度
	}
	
}

/*任务2 0.5s为间隔LED闪烁*/
void task2(void* pvParameters)
{
	while(1)
	{
		LED1 = ~LED1;
		vTaskDelay(500);//FreeRTOS延时函数,延时期间会引起任务调度
	}
}
	
  • 上面的函数并不复杂,只是出现了很多定义的变量,现将其变量定义部分列出如下。
TaskHandle_t  Start_Task_Handler;//开始任务句柄
#define START_STACK_SIZE 100 //开始任务堆栈大小
#define START_TASK_PRIO   1 //开始任务优先级
StackType_t  startStack[START_STACK_SIZE];//开始任务堆栈
StaticTask_t startTaskBuffer; //开始任务控制块
void start_task(void* pvParameters); //函数声明

TaskHandle_t  Task1_Task_Handler;//task1任务句柄
#define TASK1_STACK_SIZE  100 //task1 堆栈大小
#define TASK1_TASK_PRIO   3 //task1 优先级
StackType_t  task1Stack[TASK1_STACK_SIZE];//task1 堆栈
StaticTask_t task1TaskBuffer; //task1 控制块
void task1(void* pvParameters); //task1 声明

TaskHandle_t  Task2_Task_Handler;
#define TASK2_STACK_SIZE  100
#define TASK2_TASK_PRIO   2
StackType_t  task2Stack[TASK2_STACK_SIZE];
StaticTask_t task2TaskBuffer;
void task2(void* pvParameters);
  • 接下来就是main函数里的内容了
int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 	 
	delay_init();	    				//延时函数初始化	  
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	
	 //创建task1 和task2  创建完成后删除start_task 
	Start_Task_Handler = xTaskCreateStatic((TaskFunction_t) start_task,
									  (char*				 ) "start_task",
									  (uint32_t			 ) START_STACK_SIZE,
									  (void*				 ) NULL,
									  (UBaseType_t	 ) START_TASK_PRIO,
									  (StackType_t*	 ) startStack,
									  (StaticTask_t* ) &startTaskBuffer);

  vTaskStartScheduler();          //开启任务调度
}
  • 主函数并不复杂,通过调用start_task创建task1和task2两个任务,并删除start_task任务,以免重复创建。之后开启任务调度。
  1. 到了这里,任务创建就完成了,我们编译一下。

在这里插入图片描述

  • 编译器报了两个错误(当我们支持了静态创建任务的时候就需要补充这两个函数的定义),意思是这两个函数未定义,通过观察发现,这两个函数是与空闲任务内存和定时器任务内存相关的。前面我们说过,空闲任务和定时器任务是两个特殊任务,所以我们同样需要为其分配内存。追溯其函数原型如下:
void vApplicationGetIdleTaskMemory( StaticTask_t   **ppxIdleTaskTCBBuffer,//任务控制块
                                    StackType_t    **ppxIdleTaskStackBuffer, //任务堆栈
                                     uint32_t      *pulIdleTaskStackSize )//任务堆栈大小

所以补充函数定义如下

static StackType_t   IdleTaskStackBuffer[configMINIMAL_STACK_SIZE]; //空闲任务堆栈指针
static StaticTask_t  IdleTaskTCBBuffer;	//空闲任务控制块
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
																    StackType_t  **ppxIdleTaskStackBuffer, 
																		uint32_t      *pulIdleTaskStackSize )
{
	*ppxIdleTaskTCBBuffer   = &IdleTaskTCBBuffer;//空闲任务控制块
	*ppxIdleTaskStackBuffer = IdleTaskStackBuffer;//空闲任务堆栈
	*pulIdleTaskStackSize   = configMINIMAL_STACK_SIZE; //空闲任务堆栈大小
}

  • 定时器任务同样作此处理,这里不再赘述了。这样经过处理后,再次编译,没有报错。这样静态任务创建和删除测试就全部完成了。

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

  • 相比上面的静态任务创建,动态方法创建任务就简单很多。主要体现在以下几点。
    • 动态方法创建任务时,内存都有操作系统来管理,用户只需要指定申请空间大小和优先级即可。
    • 动态方法创建任务不需要用户再为空闲任务和定时器任务申请空间,操作系统已经申请好了,申请空间大小和任务优先级可以在FreeRTOS.h文件中来修改和配置。
  • 有了前面静态方法创建任务的基础,接下来我们分析一下动态方法创建任务所调用的API。
BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode, //任务名
							const char * const pcName,//任务标识符
							const uint16_t usStackDepth,//任务堆栈大小
							void * const pvParameters,//任务形参
							UBaseType_t uxPriority,//任务优先级
							TaskHandle_t * const pxCreatedTask )//任务句柄
  • 相比静态方法创建任务,动态方法创建任务的API更为简单了。这里直接给出相关代码,不再进行分析。
#define START_TASK_SIZE 120  // 开始任务堆栈大小
#define START_TASK_PRIO 1    // 开始任务优先级
TaskHandle_t   StartTask_Handler;// 开始任务句柄
void StartTask(void* pvParameters);

#define TASK1_TASK_SIZE 120
#define TASK1_TASK_PRIO 2
TaskHandle_t   Task1_Handler;
void Task1(void* pvParameters);

#define TASK2_TASK_SIZE 120
#define TASK2_TASK_PRIO 3
TaskHandle_t   Task2_Handler;
void Task2(void* pvParameters);
	

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 	 
	delay_init();	    				//延时函数初始化	  
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	
	 
	//创建开始任务
    xTaskCreate((TaskFunction_t )StartTask,            //任务函数
                (const char*    )"StartTask",          //任务名称
                (uint16_t       )START_TASK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}



void StartTask(void* pvParameters)
{
	 xTaskCreate((TaskFunction_t )Task1,            //任务函数
                (const char*    )"Task1",          //任务名称
                (uint16_t       )TASK1_TASK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )TASK1_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&Task1_Handler);   //任务句柄  


	 xTaskCreate((TaskFunction_t )Task2,            //任务函数
                (const char*    )"Task2",          //任务名称
                (uint16_t       )TASK2_TASK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )TASK2_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&Task2_Handler);   //任务句柄    
	vTaskDelete(StartTask_Handler);
}


void Task1(void* pvParameters)
{
	u8 i=0;
	while(1)
	{
		i++;
		printf("Task1 running %d\r\n",i);
		if (i==20)
		{
			vTaskDelete(Task2_Handler);
			printf("Task2 Deleted!!!\r\n");
		}
		LED0 = ~LED0;
		
		vTaskDelay(88);
	}
}

void Task2(void* pvParameters)
{
	u8 i=0;
	while(1)
	{
		i++;
		printf("Task2 running %d\r\n",i);
		LED1 = ~LED1;
		
		vTaskDelay(88);
	}
}

  • 代码逻辑很简单,task1每隔88ms LED状态翻转一次,翻转20次后删除task2,task2每隔88ms LED状态翻转一次,start_task中创建了task1和task2两个任务,创建完成后start_task任务删除。主函数中创建start_task任务后就开始调度器调度任务。

3、任务挂起和解挂

  • 任务挂起
/*函数声明*/
void vTaskSuspend( TaskHandle_t xTaskToSuspend )

任务挂起函数很简单,形参为任务句柄,当我们需要挂起某任务时,只需要传入该任务的形参,则该任务就会被挂起,调度器不会再调用该任务。

  • 任务解挂
/*函数声明*/
void vTaskResume( TaskHandle_t xTaskToResume )

任务解挂同样也很简单,只需要传入任务句柄,就能将挂起的函数恢复,从而使调度器能够调用该任务。


到这里我们就将FreeRTOS任务相关的基础部分讲述完了。我们可以看到FreeRTOS为我们提供了静态和动态两种方法来创建任务,相对来说,动态方法创建任务更简单快捷,也是今后常用的一种方法。任务句柄是任务的控制器,我们利用任务句柄完成对任务的一系列操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值