FreeRTOS 任务创建与删除
1. 任务创建方式
FreeRTOS 提供动态和静态两种任务创建方式。两者都会初始化任务控制块(TCB)和任务栈,并将任务加入就绪列表;主要区别是内存由谁提供。
| 方式 | API | TCB 和栈内存来源 | 特点 |
|---|---|---|---|
| 动态创建 | xTaskCreate() |
FreeRTOS 堆 | 使用方便;创建失败时要检查返回值,且堆空间不足会导致失败 |
| 静态创建 | xTaskCreateStatic() |
应用程序提供 | 内存位置和生命周期由应用程序控制;不依赖运行时堆分配 |
任务函数通常采用以下形式。任务结束时应自行删除,或持续运行;不能像普通函数一样 return:
void vMyTask(void *pvParameters)
{
for (;;) {
/* 任务工作 */
}
}
2. 动态创建任务
API:xTaskCreate()
中文含义:动态创建一个任务。 内核从 FreeRTOS 管理的堆中分配任务控制块和任务栈。
BaseType_t xTaskCreate(
TaskFunction_t pxTaskCode,
const char * const pcName,
configSTACK_DEPTH_TYPE usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask
);
| 参数 | 含义 |
|---|---|
pxTaskCode |
任务函数入口 |
pcName |
任务名,主要用于调试;长度受 configMAX_TASK_NAME_LEN 限制 |
usStackDepth |
任务栈深度,单位是 StackType_t 的个数(通常为字,不是字节) |
pvParameters |
传给任务函数的参数;不需要参数时传 NULL |
uxPriority |
任务优先级,数值越大优先级越高 |
pxCreatedTask |
输出任务句柄;不需要句柄时传 NULL |
返回 pdPASS 表示成功;返回其他值表示创建失败。实际项目中应检查结果,并依据目标平台、任务调用深度和中断情况合理设置栈大小。
TaskHandle_t xTaskHandle = NULL;
if (xTaskCreate(vMyTask,
"myTask",
256,
NULL,
tskIDLE_PRIORITY + 1,
&xTaskHandle) != pdPASS) {
/* 创建失败:检查堆空间及配置 */
}
启用动态创建需要在 FreeRTOSConfig.h 中配置:
#define configSUPPORT_DYNAMIC_ALLOCATION 1
3. 静态创建任务
API:xTaskCreateStatic()
中文含义:使用应用程序提供的内存静态创建一个任务。 应用程序提供任务栈数组和静态任务控制块对象,二者在任务运行期间必须保持有效。
TaskHandle_t xTaskCreateStatic(
TaskFunction_t pxTaskCode,
const char * const pcName,
configSTACK_DEPTH_TYPE uxStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
StackType_t *puxStackBuffer,
StaticTask_t *pxTaskBuffer
);
参数含义与 xTaskCreate() 基本相同,另外:
-
puxStackBuffer:应用程序提供的任务栈缓冲区。 -
pxTaskBuffer:应用程序提供的静态 TCB 缓冲区。
成功时返回任务句柄;失败时返回 NULL。示例:
static StackType_t xMyTaskStack[256];
static StaticTask_t xMyTaskTCB;
TaskHandle_t xTaskHandle = xTaskCreateStatic(
vMyTask,
"myTask",
256,
NULL,
tskIDLE_PRIORITY + 1,
xMyTaskStack,
&xMyTaskTCB
);
configASSERT(xTaskHandle != NULL);
启用静态创建需要配置:
#define configSUPPORT_STATIC_ALLOCATION 1
部分移植或配置还要求应用程序提供空闲任务内存;启用软件定时器时,也可能要求提供定时器服务任务内存。接口形式如下,具体是否需要实现以所用 FreeRTOS 版本和 FreeRTOSConfig.h 为准:
void vApplicationGetIdleTaskMemory(
StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
configSTACK_DEPTH_TYPE *pulIdleTaskStackSize
);
void vApplicationGetTimerTaskMemory(
StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
configSTACK_DEPTH_TYPE *pulTimerTaskStackSize
);
4. 删除任务
API:vTaskDelete()
中文含义:删除指定任务。 删除任务后,它不再参与调度。
void vTaskDelete(TaskHandle_t xTaskToDelete);
-
传入其他任务的句柄:删除该任务。
-
传入
NULL:删除当前正在运行的任务。 -
如果要删除动态创建的任务,任务控制块和栈的回收由空闲任务完成,因此空闲任务必须获得运行机会。
-
如果删除自身,
vTaskDelete(NULL)之后不应再访问该任务的局部状态或继续执行任务逻辑。 -
任务自行申请的其他资源不由
vTaskDelete()自动释放,应在删除前自行清理。 -
静态任务使用的 TCB 和栈由应用程序持有,不会由内核释放;确保缓冲区在任务删除前一直有效。
示例:
vTaskDelete(xTaskHandle); /* 删除句柄对应的任务 */
删除当前任务:
vTaskDelete(NULL);
启用该 API 需要配置:
#define INCLUDE_vTaskDelete 1
5. 任务创建和删除的工作过程
创建任务时,内核准备 TCB 和栈、初始化任务上下文,并把任务加入对应优先级的就绪列表。若调度器已运行且新任务优先级更高,内核可能立即触发任务切换。
删除任务时,内核根据任务句柄找到任务并将其从调度相关列表中移除。删除其他任务时可直接清理其内核对象;删除自身时,内存清理会延后到空闲任务执行。
6. 使用要点
-
动态创建时检查返回值,并关注 FreeRTOS 堆空间;静态创建时保证 TCB 和栈缓冲区的生命周期覆盖任务整个生命周期。
-
xTaskCreate()和xTaskCreateStatic()的栈深度参数按StackType_t个数计,不要直接当作字节数。 -
任务优先级数值越大,优先级越高;实际可用范围由
configMAX_PRIORITIES决定。 -
删除动态任务后,空闲任务负责回收内核分配的内存;用户自行分配的资源需要单独释放。
-
通常优先使用动态创建以简化代码;若需要确定内存占用、控制内存位置或避免运行时堆分配,可选择静态创建。
7. 本章函数汇总
| 函数 | 中文含义 | 主要用法 |
|---|---|---|
xTaskCreate() |
动态创建任务 | xTaskCreate(task_fn, "name", stack_depth, param, priority, &handle);返回 pdPASS 表示成功 |
xTaskCreateStatic() |
静态创建任务 | xTaskCreateStatic(task_fn, "name", stack_depth, param, priority, stack_buffer, &tcb_buffer);返回任务句柄,失败返回 NULL |
vTaskDelete() |
删除任务 | vTaskDelete(handle) 删除指定任务;vTaskDelete(NULL) 删除当前任务 |
vApplicationGetIdleTaskMemory() |
提供空闲任务的静态内存 | 按接口参数返回空闲任务的 TCB 缓冲区、栈缓冲区和栈深度;仅在相应配置要求时实现 |
vApplicationGetTimerTaskMemory() |
提供定时器服务任务的静态内存 | 按接口参数返回定时器任务的 TCB 缓冲区、栈缓冲区和栈深度;启用软件定时器且配置要求时实现 |
vMyTask()(示例任务函数) |
任务执行入口 | 定义为 void task_fn(void *pvParameters),由创建函数传入;持续运行或在结束前调用 vTaskDelete(NULL) |