STM32F1+HAL库+FreeTOTS学习3——任务创建(动态和静态两种)
- 任务创建API函数
- 任务创建流程
- 代码实现
- 1. 动态任务创建和删除
- 2. 静态任务创建和删除
上期我们学习了STM32移植FreeRTOS搭建基准工程,现在我们来学习任务创建
任务创建API函数
前面我们了解到,FreeRTOS相对于裸机开发,最大的不同在与支持多任务同时运行,那么FreeRTOS上运行多少个任务,任务运行的优先级、堆栈大小,这些都是怎么确定?当然是我们自己去创建任务,自己确定。。。。。。。
FreeRTOS的任务创建删除本质就是调用FreeRTOS的API函数,分为动态创建和静态创建两种:
- 动态创建的任务,由FreeRTOS自己从管理的堆栈中分配,用户只需要确定堆栈大小即可。
- 静态创建的任务,由用户自己定义堆栈,将定义好的堆栈分配给任务。这部分堆栈由用户控制和管理,不受FreeRTOS的管控。
具体API函数如下:
- 动态创建任务函数
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,表示创建失败
*/
- 静态创建任务
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:表示任务创建失败
返回值为其他值:任务句柄,任务创建成功
*/
- 任务删除函数
- 被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除。
- 当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)
- 空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存, 则需要由用户在任务被删除前提前释放,否则将导致内存泄露
void vTaskDelete(TaskHandle_t xTaskToDelete);
/*
参数为待删除任务的句柄,用于删除已
*/
任务创建流程
动态创建任务函数创建流程:
- 将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1
- 定义函数入口参数
- 编写任务函数
- 完成以上三个步骤即可完成一个任务的动态创建,任务创建完毕后任务会立刻进入就绪状态,由任务调度器控制并运行,内部实现方法如下:
静态创建任务使用流程
- 需将宏configSUPPORT_STATIC_ALLOCATION 配置为 1
- 定义空闲任务&定时器任务的任务堆栈及TCB
- 实现两个接口函数:vApplicationGetIdleTaskMemory( ) 和vApplicationGetTimerTaskMemory ( )
- 定义函数入口参数
- 编写任务函数
- 任务创建完毕后任务会立刻进入就绪状态,由任务调度器控制并运行,内部实现方法如下:
删除任务流程
- 使用删除任务函数,需将宏INCLUDE_vTaskDelete 配置为 1
- 入口参数输入需要删除的任务句柄(NULL代表删除本身)
- 内部实现方式如下:
代码实现
1. 动态任务创建和删除
- 在main.c中调用如下函数,进入FreeRTOS操作系统。
freertos_demo();
- 在freertos_demo.c里面编写动态任务创建,并编写任务功能函数
#include "freertos_demo.h"
#include "main.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 */
/******************************************************************************************************/
/* LCD刷屏时使用的颜色 */
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
xTaskCreate((TaskFunction_t )start_task, /* 任务函数 */
(const char* )"start_task", /* 任务名称 */
(uint16_t )START_STK_SIZE, /* 任务堆栈大小 */
(void* )NULL, /* 传入给任务函数的参数 */
(UBaseType_t )START_TASK_PRIO, /* 任务优先级 */
(TaskHandle_t* )&StartTask_Handler); /* 任务句柄 */
vTaskStartScheduler(); //开启任务调度
}
/**
* @brief start_task
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区,关闭中断,此时停止任务调度*/
/* 创建任务1 */
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
/* 创建任务2 */
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区,重新开启中断,开启任务调度 */
}
/**
* @brief task1
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task1(void *pvParameters)
{
while(1)
{
HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
printf("task1 start\r\n");
/* LED0闪烁 */
vTaskDelay(1000); /* 延时1000ticks */
}
}
/**
* @brief task2
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task2(void *pvParameters)
{
float float_num = 0.0;
while(1)
{
float_num += 0.01f; /* 更新数值 */
printf("float_num: %0.4f\r\n", float_num); /* 打印数值 */
printf("task2 start\r\n");
vTaskDelay(1000); /* 延时1000ticks */
}
}
2. 静态任务创建和删除
- 动态任务创建
在main.c中调用如下函数,进入FreeRTOS操作系统。
freertos_demo();
- 在freertos_demo.c里面编写静态任务创建,并编写任务功能函数
#include "freertos_demo.h"
#include "main.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
StackType_t StartTask_Stack[START_STK_SIZE]; /*任务堆栈*/
StaticTask_t StartTask_TCB; /*任务控制块*/
void start_task(void *pvParameters); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
StackType_t Task1_Stack[TASK1_STK_SIZE]; /*任务堆栈*/
StaticTask_t Task1_TCB; /*任务控制块*/
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
StackType_t Task2_Stack[TASK2_STK_SIZE]; /*任务堆栈*/
StaticTask_t Task2_TCB; /*任务控制块*/
void task2(void *pvParameters); /* 任务函数 */
/******************************************************************************************************/
/* 空闲任务内存分配*/
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, //任务控制块内存
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize ) /*lint !e526 Symbol not defined as it is an application callback. */
{
* ppxIdleTaskTCBBuffer = &idle_task_tcb;
* ppxIdleTaskStackBuffer = idle_task_stack;
* pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
/* 软件定时器内存分配*/
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
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)
{
//静态创建任务函数,返回值为任务句柄
StartTask_Handler = xTaskCreateStatic( (TaskFunction_t) start_task, //指向任务函数的指针
(const char * const)"start_task", //任务函数名
(const uint32_t) START_STK_SIZE, //任务堆栈大小,单位为字节
(void * const) NULL, //传递的任务函数参数
(UBaseType_t) START_TASK_PRIO, //任务优先级
(StackType_t * const) StartTask_Stack, //任务堆栈,一般为数组,用户自己分配
(StaticTask_t * const) &StartTask_TCB ); //任务控制块指针,由用户分配
vTaskStartScheduler(); //开启任务调度
}
/**
* @brief start_task
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区,关闭中断,此时停止任务调度*/
/* 创建任务1,静态的方式 */
Task1Task_Handler = xTaskCreateStatic( (TaskFunction_t ) task1, //指向任务函数的指针
( char * ) "task1", //任务函数名
( uint32_t ) TASK1_STK_SIZE, //任务堆栈大小,单位为字节
(void * ) NULL, //传递的任务函数参数
(UBaseType_t ) TASK1_PRIO, //任务优先级
(StackType_t * ) Task1_Stack, //任务堆栈,一般为数组,用户自己分配
(StaticTask_t * ) &Task1_TCB ); //任务控制块指针,由用户分配
/* 创建任务2,静态的方式 */
Task2Task_Handler = xTaskCreateStatic( (TaskFunction_t ) task2, //指向任务函数的指针
( char * ) "task2", //任务函数名
( uint32_t ) TASK2_STK_SIZE, //任务堆栈大小,单位为字节
(void * ) NULL, //传递的任务函数参数
(UBaseType_t ) TASK2_PRIO, //任务优先级
(StackType_t * ) Task2_Stack, //任务堆栈,一般为数组,用户自己分配
(StaticTask_t * ) &Task2_TCB ); //任务控制块指针,由用户分配
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区,重新开启中断,开启任务调度 */
}
/**
* @brief task1
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task1(void *pvParameters)
{
while(1)
{
HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
printf("task1 start\r\n");
/* LED0闪烁 */
vTaskDelay(1000); /* 延时1000ticks */
}
}
/**
* @brief task2
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task2(void *pvParameters)
{
float float_num = 0.0;
while(1)
{
float_num += 0.01f; /* 更新数值 */
printf("float_num: %0.4f\r\n", float_num); /* 打印数值 */
printf("task2 start\r\n");
vTaskDelay(1000); /* 延时1000ticks */
}
}
静态实现和动态实现的代码作用是一样的,这里统一解释以下:
- 创建了一个开始任务,在开始任务中创建任务1和任务2,创建完成后删除开始任务。
- 任务1执行LED闪烁,周期为1S
- 任务2执行串口打印,每一秒打一次信息。