云计算百科
云计算领域专业知识百科平台

FreeRTOS任务创建

1 任务创建与删除的基本概念

任务创建:在FreeRTOS中创建任务,本质上是调用相应的API函数,分配任务控制块(TCB)和任务栈空间。
任务删除:移除任务从就绪、阻塞、挂起和事件列表中,释放相关内存(动态创建时由空闲任务处理)。

2 任务创建与删除的API函数

xTaskCreate():动态创建任务,内存由FreeRTOS堆分配。
xTaskCreateStatic():静态创建任务,内存由用户分配。
vTaskDelete():删除任务,动态或静态创建的任务均可删除。

3. 动态创建任务流程(xTaskCreate)

3.1 使用步骤

1. 将configSUPPORT_DYNAMIC_ALLOCATION设为1。
2. 定义任务函数(入口参数)。
3. 调用xTaskCreate()。

3.2 内部实现

1. 申请堆栈和TCB内存。
2. 初始化TCB成员(优先级、名称、栈顶等)。
3. 初始化任务栈(填充0xA5,设置上下文)。
4. 将任务添加到就绪列表。
5. 更新就绪优先级位图。
6. 如果调度器已运行且新任务优先级更高,触发任务切换。

4 静态创建任务流程(xTaskCreateStatic)

4.1 使用步骤

1. 将configSUPPORT_STATIC_ALLOCATION设为1。
2. 用户提供栈数组和TCB结构体。
3. 定义任务函数。
4. 调用xTaskCreateStatic()。

4.2 内部实现

1. 用户传入栈和TCB指针。
2. 初始化TCB(类似动态方式)。
3. 添加到就绪列表。

5 任务删除流程(vTaskDelete)

5.1 使用步骤

1. 将INCLUDE_vTaskDelete设为1。
2. 调用vTaskDelete(),传入任务句柄(NULL表示删除自身)。

5.2 内部实现

1. 从所有列表中移除任务。
2. 如果是删除自身,则进入等待删除列表,由空闲任务释放内存。
3. 如果是删除其他任务,直接释放内存。
4. 更新任务数量和阻塞时间。
5. 可能触发任务切换。

6 动态与静态创建对比

动态创建:内存来自FreeRTOS堆,灵活常用,需配置configSUPPORT_DYNAMIC_ALLOCATION=1。
静态创建:内存由用户分配,无分配失败风险,需配置configSUPPORT_STATIC_ALLOCATION=1,适用于内存受限或需固定地址的场景。

7 关键注意事项

1. 优先级数值越大优先级越高。
2. 栈大小以字为单位(如128表示128*4字节)。
3. 删除自身时传入NULL,内存由空闲任务释放。
4. 用户申请的内存需在任务删除前释放,否则会导致内存泄漏。
5. 关键代码段可使用临界区保护,防止被中断打断。

8 任务动态创建与删除实验

8.1 xTaskCreate() 函数

这是FreeRTOS中动态创建任务的核心API函数。其作用是从FreeRTOS管理的堆内存中自动分配任务控制块(TCB)和任务栈空间,并将新任务初始化为就绪状态,等待调度器调度。

函数原型:
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
configSTACK_DEPTH_TYPE usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask );

参数说明:
pvTaskCode: 指向任务函数的指针,任务函数通常为无限循环
pcName: 任务名称字符串,用于调试和标识
usStackDepth: 任务栈深度(以字为单位,如128表示128*4字节)
pvParameters: 传递给任务函数的参数指针
uxPriority: 任务优先级(0为最低,数值越大优先级越高)
pxCreatedTask: 返回的任务句柄,可用于后续任务管理

返回值:
pdPASS: 任务创建成功
pdFAIL: 任务创建失败(通常因内存不足)

特点:
任务创建后立即进入就绪状态
内存由系统自动管理
需要配置configSUPPORT_DYNAMIC_ALLOCATION=1

8.2 vTaskDelete() 函数

这是FreeRTOS中删除任务的API函数。当传入参数为NULL时,表示删除当前正在执行的任务自身。

工作原理:
任务从所有状态列表(就绪、阻塞、挂起、事件)中移除
任务自身进入等待删除列表
空闲任务(Idle Task)会在后续运行中释放该任务占用的系统内存

注意事项:
删除自身后,任务函数立即停止执行
用户手动申请的内存(如malloc)不会被自动释放,需要在删除前手动释放
删除其他任务时,需传入有效的任务句柄
需要配置INCLUDE_vTaskDelete=1

使用场景:
一次性任务完成后自我清理
任务异常时的安全退出
动态任务管理系统中的任务移除

8.3 临界区

临界区是一段在执行期间不能被中断的代码区域,用于保护共享资源的原子性访问。

相关函数:
taskENTER_CRITICAL(): 进入临界区,关闭FreeRTOS管理的中断
taskEXIT_CRITICAL(): 退出临界区,恢复中断

作用机制:
进入临界区后,FreeRTOS会关闭可管理的中断(通常是PendSV和SysTick)
但仍可响应高优先级硬件中断(如外设中断)
退出临界区时恢复之前的中断状态

主要用途:
保护多任务共享的全局变量或数据结构
防止任务切换期间的资源竞争
确保代码段的原子性执行

注意事项:
临界区应尽量简短,避免影响系统实时性
不可嵌套调用(或需注意嵌套计数)
不能在临界区内调用可能引起任务阻塞的API(如vTaskDelay)

8.4 实验代码

/*
* 任务优先级定义:
* 数值越大优先级越高,FreeRTOS中优先级通常从0开始,0为最低优先级
* 这里定义起始任务优先级为1,task1优先级为2,task2优先级为3,task3优先级为4
*/
#define START_TASK_PRIO 1 // 起始任务优先级
#define START_TASK_STACK_SIZE 128 // 起始任务堆栈大小(以字为单位,128字=128*4字节=512字节)
TaskHandle_t start_task_handler; // 起始任务句柄,用于任务管理
void start_task(void *pvParameters); // 起始任务函数声明

#define TASK1_PRIO 2 // 任务1优先级
#define TASK1_STACK_SIZE 128 // 任务1堆栈大小
TaskHandle_t task1_handler; // 任务1句柄
void task1(void *pvParameters); // 任务1函数声明

#define TASK2_PRIO 3 // 任务2优先级
#define TASK2_STACK_SIZE 128 // 任务2堆栈大小
TaskHandle_t task2_handler; // 任务2句柄
void task2(void *pvParameters); // 任务2函数声明

#define TASK3_PRIO 4 // 任务3优先级
#define TASK3_STACK_SIZE 128 // 任务3堆栈大小
TaskHandle_t task3_handler; // 任务3句柄
void task3(void *pvParameters); // 任务3函数声明

/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
* 功能:创建起始任务并启动FreeRTOS调度器
*/
void freertos_demo(void)
{
/* 使用xTaskCreate动态创建起始任务
* 参数说明:
* 1. start_task – 任务函数指针
* 2. "start_task" – 任务名称(字符串)
* 3. START_TASK_STACK_SIZE – 任务堆栈大小
* 4. NULL – 传递给任务函数的参数(此处为空)
* 5. START_TASK_PRIO – 任务优先级
* 6. &start_task_handler – 返回的任务句柄指针
*/
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);

/* 启动FreeRTOS调度器,系统开始进行任务调度 */
vTaskStartScheduler();
}

/**
* @brief 起始任务函数
* @param pvParameters – 任务参数(此处未使用)
* @retval 无
* 功能:创建task1、task2、task3三个任务,然后删除自身
*/
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);

/* 退出临界区 */
taskEXIT_CRITICAL();

/* 删除起始任务自身(NULL表示删除当前任务) */
vTaskDelete(NULL);
}

/**
* @brief 任务1函数
* @param pvParameters – 任务参数(此处未使用)
* @retval 无
* 功能:控制LED0每500ms闪烁一次
*/
void task1(void *pvParameters)
{
while (1)
{
LED0_TOGGLE(); // LED0状态翻转
vTaskDelay(500); // 延迟500ms(以FreeRTOS的节拍为单位)
}
}

/**
* @brief 任务2函数
* @param pvParameters – 任务参数(此处未使用)
* @retval 无
* 功能:控制LED1每500ms闪烁一次
*/
void task2(void *pvParameters)
{
while (1)
{
LED1_TOGGLE(); // LED1状态翻转
vTaskDelay(500); // 延迟500ms
}
}

/**
* @brief 任务3函数
* @param pvParameters – 任务参数(此处未使用)
* @retval 无
* 功能:检测按键KEY0,按下则删除task1任务
*/
void task3(void *pvParameters)
{
uint8_t key = 0;

while (1)
{
printf("task3正在运行!!!\\r\\n"); // 输出运行提示信息

key = key_scan(0); // 扫描按键(参数0表示不支持连按)

if (key == KEY0_PRES) // 如果KEY0按下
{
if (task1_handler != NULL) // 检查task1句柄是否有效
{
printf("删除task1任务\\r\\n");
vTaskDelete(task1_handler); // 删除task1任务
task1_handler = NULL; // 将句柄置为NULL,防止重复删除
}
}
vTaskDelay(10); // 延迟10ms,控制按键检测频率
}
}

实现功能:首先创建一个起始任务来初始化三个功能任务,其中 task1 和 task2 分别控制 LED0 和 LED1 以 500ms 间隔闪烁,实现双灯交替闪烁效果;task3 则负责监控按键 KEY0 的按下事件,并在检测到按键时动态删除 task1 任务,演示了任务运行时管理的功能。

9 任务动态创建与删除实验

9.1 xTaskCreateStatic()函数

xTaskCreateStatic 是 FreeRTOS 中用于创建静态内存分配任务的函数。与动态分配的 xTaskCreate 不同,它需要用户预先分配任务控制块(TCB)和栈内存,适合在禁用动态内存分配的系统中使用。

函数原型:
TaskHandle_t xTaskCreateStatic( TaskFunction_t pvTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer );

参数说明:
pvTaskCode: 指向任务函数的指针,任务函数通常为无限循环
pcName: 任务名称字符串,用于调试和标识
ulStackDepth: 任务栈深度(以字为单位,如128表示128*4字节)
pvParameters: 传递给任务函数的参数指针
uxPriority: 任务优先级(0为最低,数值越大优先级越高)
puxStackBuffer: 指向任务栈缓冲区的指针,必须由用户静态分配
pxTaskBuffer: 指向任务控制块(TCB)缓冲区的指针,必须由用户静态分配

返回值:
非NULL: 任务创建成功,返回任务句柄
NULL: 任务创建失败(通常因为puxStackBuffer或pxTaskBuffer为NULL,或栈深度为0)

特点:
任务创建后立即进入就绪状态
内存由用户预先静态分配,无动态内存管理开销
需要配置configSUPPORT_STATIC_ALLOCATION=1
无内存碎片问题,适合长期运行的安全关键系统

9.2 实验代码

/* FreeRTOS静态任务创建示例代码 */

/* 起始任务配置定义 */
#define START_TASK_PRIO 1 // 任务优先级
#define START_TASK_STACK_SIZE 128 // 堆栈大小(以字为单位)
TaskHandle_t start_task_handler; // 起始任务句柄
StackType_t START_TASK_STACK[START_TASK_STACK_SIZE]; // 起始任务堆栈数组(静态分配)
StaticTask_t start_task_tcb; // 起始任务控制块(TCB)
void start_task(void *pvParameters); // 起始任务函数声明

/* 任务1配置定义 */
#define TASK1_PRIO 2 // 任务优先级
#define TASK1_STACK_SIZE 128 // 堆栈大小
TaskHandle_t task1_handler; // 任务1句柄
StackType_t TASK1_STACK[TASK1_STACK_SIZE]; // 任务1堆栈数组(注意:这里误写为START_TASK_STACK_SIZE,应该是TASK1_STACK_SIZE)
StaticTask_t task1_tcb; // 任务1控制块
void task1(void *pvParameters); // 任务1函数声明

/* 任务2配置定义 */
#define TASK2_PRIO 3 // 任务优先级
#define TASK2_STACK_SIZE 128 // 堆栈大小
TaskHandle_t task2_handler; // 任务2句柄
StackType_t TASK2_STACK[TASK2_STACK_SIZE]; // 任务2堆栈数组(注意:同样误写为START_TASK_STACK_SIZE)
StaticTask_t task2_tcb; // 任务2控制块
void task2(void *pvParameters); // 任务2函数声明

/* 任务3配置定义 */
#define TASK3_PRIO 3 // 任务优先级(与任务2相同,这可能导致任务调度不符合预期)
#define TASK3_STACK_SIZE 128 // 堆栈大小
TaskHandle_t task3_handler; // 任务3句柄
StackType_t TASK3_STACK[TASK3_STACK_SIZE]; // 任务3堆栈数组(同样误写为START_TASK_STACK_SIZE)
StaticTask_t task3_tcb; // 任务3控制块
void task3(void *pvParameters); // 任务3函数声明

/* 空闲任务和定时器任务内存配置(静态分配必需) */
StaticTask_t idle_task_tcb; // 空闲任务控制块
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE]; // 空闲任务堆栈
StaticTask_t timer_task_tcb; // 定时器任务控制块
StackType_t timer_task_stack[configMINIMAL_STACK_SIZE]; // 定时器任务堆栈

// 空闲任务内存分配接口函数(FreeRTOS静态创建任务时必须实现)
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer = &idle_task_tcb; // 返回空闲任务控制块指针
*ppxIdleTaskStackBuffer = idle_task_stack; // 返回空闲任务堆栈指针
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; // 返回空闲任务堆栈大小
}

// 定时器任务内存分配接口函数(使用软件定时器时必须实现)
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
{
*ppxTimerTaskTCBBuffer = &timer_task_tcb; // 返回定时器任务控制块指针
*ppxTimerTaskStackBuffer = timer_task_stack; // 返回定时器任务堆栈指针
*pulTimerTaskStackSize = configMINIMAL_STACK_SIZE; // 返回定时器任务堆栈大小
}

/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
* 功能:使用静态方式创建起始任务并启动FreeRTOS调度器
*/
void freertos_demo(void)
{
// 使用xTaskCreateStatic静态创建起始任务
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 // 任务控制块指针(用户分配)
);

// 启动FreeRTOS调度器
vTaskStartScheduler();
}

/**
* @brief 起始任务函数
* @param pvParameters – 任务参数(未使用)
* @retval 无
* 功能:静态创建task1、task2、task3三个任务,然后删除自身
*/
void start_task(void *pvParameters)
{
// 进入临界区,防止任务创建过程被中断打断
taskENTER_CRITICAL();

// 使用xTaskCreateStatic静态创建任务1
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 // 任务控制块
);

// 使用xTaskCreateStatic静态创建任务2
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 // 任务控制块
);

// 使用xTaskCreateStatic静态创建任务3
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 // 任务控制块
);

// 删除起始任务自身(传入任务句柄而不是NULL)
vTaskDelete(start_task_handler);

// 退出临界区(由于上面已经删除任务,此语句不会执行,存在逻辑错误)
taskEXIT_CRITICAL();
}

/**
* @brief 任务1函数
* @param pvParameters – 任务参数(未使用)
* @retval 无
* 功能:控制LED0每500ms闪烁一次
*/
void task1(void *pvParameters)
{
while (1)
{
LED0_TOGGLE(); // LED0状态翻转
vTaskDelay(500); // 延迟500ms(FreeRTOS节拍)
}
}

/**
* @brief 任务2函数
* @param pvParameters – 任务参数(未使用)
* @retval 无
* 功能:控制LED1每500ms闪烁一次
*/
void task2(void *pvParameters)
{
while (1)
{
LED1_TOGGLE(); // LED1状态翻转
vTaskDelay(500); // 延迟500ms
}
}

/**
* @brief 任务3函数
* @param pvParameters – 任务参数(未使用)
* @retval 无
* 功能:检测按键KEY0,按下则删除task1任务
*/
void task3(void *pvParameters)
{
uint8_t key = 0;
while (1)
{
printf("task3正在运行!!!\\r\\n"); // 输出运行提示

key = key_scan(0); // 扫描按键(参数0表示不支持连按)

if (key == KEY0_PRES) // 如果KEY0按下
{
if (task1_handler != NULL) // 检查任务1句柄是否有效
{
printf("删除task1任务\\r\\n");
vTaskDelete(task1_handler); // 删除任务1
task1_handler = NULL; // 将句柄置为NULL,防止重复删除
}
}
vTaskDelay(10); // 延迟10ms,控制按键检测频率
}
}

实现功能:首先创建一个起始任务来初始化三个功能任务,其中task1和task2分别控制LED0和LED1以500ms间隔闪烁,实现双灯交替闪烁效果;task3则监控按键KEY0的按下事件,并在检测到按键时动态删除task1任务,演示了运行时任务管理功能。所有任务的任务控制块和栈空间均采用静态分配方式,由用户预先定义数组和结构体,同时实现了空闲任务和定时器任务的内存分配接口。

赞(0)
未经允许不得转载:网硕互联帮助中心 » FreeRTOS任务创建
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!