FreeRTOS 创建任务

FreeRTOS创建任务的方式主要是两种:

xTaskCreate()xTaskCreateStatic()
动态创建(默认)静态创建(高级功能)
创建任务所需的内存从”堆“里分配创建任务所需的内存由用户手动分配

FreeRTOS参考手册,可以查看所有API的用法:https://freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/07-Books-and-manual/01-RTOS_book

xTaskCreate()

BaseType_txTaskCreate(TaskFunction_t pxTaskCode,const char * const pcName,const configSTACK_DEPTH_TYPE uxStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask);
返回值
返回是否执行成功
参数1
填写任务代码的函数名
参数2
任务名,调试时给人看的,调度器不使用它
参数3
栈深度,每个任务一个任务栈,默认深度128字,调用层次越多,局部体积越大,这个参数越大
参数4
传递任务代码的参数
典型用法:使用一段任务代码创建多个任务实例
参数5
任务优先级
决定了任务切换的顺序,调度器总是优先高优先级的任务来执行
优先级的范围:0~configMAX_PRIORITIES - 1,0是最低优先级
参数6
任务句柄
这是一个输出参数,返回任务句柄。
xTaskCreate()创建任务成功后,通过这个参数把句柄给你,之后的任何关于该任务的操作都要用到该句柄。

pcName 任务名

Windows -> Show View -> FreeRTOS Task List
pcName 任务名
可以监控到任务状态
任务状态

pvParameters 使用一段任务代码创建多个任务

例如之前使用LED1和LED3的不同频率闪烁:

1234567891011121314151617181920212223
void vLED1Task(void *pvParameters){	while(1)	{		HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);		vTaskDelay(pdMS_TO_TICKS(1000));		HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);		vTaskDelay(pdMS_TO_TICKS(1000));	}	vTaskDelete(NULL);} void vLED2Task(void *pvParameters){	while(1)	{		HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);		vTaskDelay(pdMS_TO_TICKS(300));		HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);		vTaskDelay(pdMS_TO_TICKS(300));	}	vTaskDelete(NULL);}

上面的代码只有引脚和延迟时间不同,想要使用同一套任务代码,传递这些差异的参数。
通过给pvParameters参数赋不同的值,传递差异。

例:编写一段任务代码,来实现开发板上的LED1和LED2同时闪烁,并让二者以不同频率闪烁,LED1每1s闪烁一次,LED2每200ms闪烁一次。

首先,定义一个名为LEDBlinkInfoTypeDef的结构体,这个结构体存储闪灯的信息。

123456
typedef struct{	GPIO_TypeDef *LED_GPIOx;//LED对应引脚的GPIO端口号	uint16_t LED_Pin;//LED对应引脚的引脚编号	uint32_t Period;//闪灯的周期} LEDBlinkInfoTypeDef;

在程序中定义两个LEDBlinkInfoTypeDef类型的常量,分别存储LED1和LED2的闪灯信息

1234
//LED1的闪灯信息,PE2,1000msstatic const LEDBlinkInfo_TypeDef led1BlinkInfo = { .LED_GPIOx = GPIOE, .LED_Pin = GPIO_PIN_2, .Period = 1000 };//LED2的闪灯信息,PE3,200msstatic const LEDBlinkInfo_TypeDef led2BlinkInfo = { .LED_GPIOx = GPIOE, .LED_Pin = GPIO_PIN_3, .Period = 200 };

编写任务函数

1234567891011
static void prvLEDTask(void *pvParameters){LEDBlinkInfo_TypeDef *info = (LEDBlinkInfo_TypeDef *)pvParameters;//把pvParameters强转为LEDBlinkInfo_TypeDef类型while(1){HAL_GPIO_WritePin(info->LED_GPIOx, info->LED_Pin, GPIO_PIN_SET);vTaskDelay(pdMS_TO_TICKS(info->Period / 2));HAL_GPIO_WritePin(info->LED_GPIOx, info->LED_Pin, GPIO_PIN_RESET);vTaskDelay(pdMS_TO_TICKS(info->Period / 2));}}

调用函数,并启用调度器

12345
/* USER CODE BEGIN 2 */xTaskCreate(prvLEDTask, "LED1", 128, (void *)&led1BlinkInfo, 1, NULL);xTaskCreate(prvLEDTask, "LED2", 128, (void *)&led2BlinkInfo, 1, NULL);vTaskStartScheduler();//启用调度器/* USER CODE END 2 */

uxPriority任务优先级

优先级的范围:0~configMAX_PRIORITIES - 1,0是最低优先级。
其中configMAX_PRIORITIES是配置文件中的一个配置项,在Core->Inc->FreeRTOSConfig.h中可以找到该配置项。

12
/* configMAX_PRIORITIES Sets the number of available task priorities. Tasks can be assigned priorities of 0 to (configMAX_PRIORITIES - 1). Zero is the lowest priority. */#define configMAX_PRIORITIES 5

这个参数决定了最大任务的数量,默认配置是5。
查看之前的2个任务,手动指定了任务的优先级是1。
任务状态
另外,IDLE是空闲任务,是任何代码都不执行时执行的任务。因此优先级最低,是0。
Tmr Svg取最高优先级4。

pxCreatedTask 返回任务句柄

暂停任务、恢复已暂停任务、删除任务

void vTaskSuspend( TaskHandle_t xTaskToSuspend );
暂停任务:参数写要暂停任务的句柄,该参数填写NULL会暂停自身。

void vTaskResume( TaskHandle_t xTaskToResume );
恢复任务:参数写要恢复的任务的句柄。

void vTaskDelete( TaskHandle_t xTaskToDelete );
删除任务:参数写要删除任务的句柄,该参数填NULL会删除自身。

例:创建LED任务,驱动LED闪烁,再创建一个Task1任务,使用这个任务来暂停、恢复、和停止闪灯的过程。
1、创建一个LED任务,让LED每200ms闪烁一次。在创建任务的过程中,通过xTaskCreate()的最后一个参数把任务句柄保存下来。
static TaskHandle_t xLEDTaskHandle;//LED的任务句柄

1234567891011
//LED的任务代码static void prvLEDTask(void *pvParameters){	for(;;)	{		HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);		vTaskDelay(pdMS_TO_TICKS(100));		HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);		vTaskDelay(pdMS_TO_TICKS(100));	}}

在main()方法里创建LED任务,保存任务句柄
xTaskCreate( prvLEDTask, "LED", 128, NULL, 1, &xLEDTaskHandle );

2、创建Task1任务,通过vTaskSuspen()、vTaskResume()、vTaskDelete()分别实现LED任务的暂停、恢复和删除。

12345678910111213141516171819
//Task1的任务代码static void prvTask1(void *pvParameters){	vTaskDelay(pdMS_TO_TICKS(1000));//延迟1s 	//暂停LED任务	vTaskSuspend(xLEDTaskHandle);	vTaskDelay(pdMS_TO_TICKS(1000));//延迟1s 	//让LED任务继续执行	vTaskResume(xLEDTaskHandle);	vTaskDelay(pdMS_TO_TICKS(1000));//延迟1s 	//删除LED任务	vTaskDelete(xLEDTaskHandle); 	//所有的代码都执行完毕,删除自身	vTaskDelete(NULL);   }

在main()方法中创建Task1任务,无需保存任务句柄,因此最后一个参数填NULL。
xTaskCreate( prvTask1, "Task1", 128, NULL, 1, NULL );

🔍 ×