--- tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第4章 任务创建和删除" author: "尚硅谷研究院" date: 2026-07-17 created: 2026-07-17 --- # 任务创建与删除 > **用生活理解**:公司招人(创建任务)需要分配工位(栈)和建立档案(TCB);人员可以按编制内(静态)或外聘(动态)两种方式入职;裁员(删除任务)时需清理工位和档案。 ## 概念介绍 ### 基本概念 FreeRTOS 中,一个**任务(Task)**包含三要素: - **任务函数**:用户编写的无限循环函数 - **堆栈(Stack)**:保存任务上下文(寄存器、局部变量) - **TCB(Task Control Block)**:任务控制块,记录任务状态、优先级等信息 ### 工作原理 FreeRTOS 创建任务时: 1. 分配栈空间和 TCB 内存 2. 将任务加入就绪链表 3. 调度器根据优先级和时间片选择运行的任务 **动态创建**(`xTaskCreate`)由内核自动分配栈和 TCB 内存: - 优点:使用简单,无需预先规划内存 - 缺点:依赖 heap 管理,需要确保堆空间充足 **静态创建**(`xTaskCreateStatic`)由用户预先分配栈和 TCB 内存: - 优点:不依赖堆分配,适合安全关键系统 - 缺点:需要提前计算任务数量并分配内存 删除任务时使用 `vTaskDelete()`,任务被删除后不会再次运行,任务占用的资源会被释放。 ### 核心API #### 动态创建:xTaskCreate | 函数 | 参数 | 功能简述 | |----------|--------|-------------| | `xTaskCreate` | `pvTaskCode`, `pcName`, `usStackDepth`, `pvParameters`, `uxPriority`, `pxCreatedTask` | 动态创建一个新任务 | | | `pvTaskCode` | 任务函数指针 | | | `pcName` | 任务名称(用于调试) | | | `usStackDepth` | 栈深度(单位 word,STM32 中 1 word = 4 字节) | | | `pvParameters` | 传递给任务函数的参数 | | | `uxPriority` | 任务优先级(0~configMAX_PRIORITIES-1) | | | `pxCreatedTask` | 返回任务句柄 | | **返回** | `pdPASS` | 创建成功 | | | `errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY` | 创建失败(内存不足) | #### 静态创建:xTaskCreateStatic | 函数 | 参数 | 功能简述 | |----------|--------|-------------| | `xTaskCreateStatic` | `pvTaskCode`, `pcName`, `ulStackDepth`, `pvParameters`, `uxPriority`, `puxStackBuffer`, `pxTaskBuffer` | 静态创建一个新任务 | | | `puxStackBuffer` | 用户预先分配的栈空间 | | | `pxTaskBuffer` | 用户预先分配的 TCB 空间 | | **返回** | 非 NULL | 任务句柄 | | | NULL | 创建失败(用户未提供内存) | #### 删除任务:vTaskDelete | 函数 | 参数 | 功能简述 | |----------|--------|-------------| | `vTaskDelete` | `xTask` | 删除指定任务 | | | `xTask` | 待删除任务句柄;传 NULL 表示删除自身 | | **注意** | 传 NULL 时会在函数末尾触发一次任务切换 | | #### 空闲任务与定时器任务内存钩子(静态分配) 当 `configSUPPORT_STATIC_ALLOCATION = 1` 时,必须提供以下两个钩子函数: | 函数 | 功能简述 | |----------|-------------| | `vApplicationGetIdleTaskMemory` | 提供空闲任务的 TCB 和栈缓冲区 | | `vApplicationGetTimerTaskMemory` | 提供定时器任务的 TCB 和栈缓冲区 | ## 实验 ### 需求描述 - P01:动态创建 3 个任务,task1 翻转 LED1,task2 翻转 LED2,task3 检测按键删除 task1 - P02:使用静态创建实现同样功能,并提供空闲任务和定时器任务的内存 ### 代码实现 #### P01:动态创建 **代码来源:**03_代码/01_动态创建任务和删除/Core/Src/freertos_demo.c ```c #include "freertos_demo.h" #include "FreeRTOS.h" #include "task.h" #include "LED.h" #include "Key.h" /* 起始任务配置 */ #define START_TASK_STACK 128 #define START_TASK_PRIORITY 1 TaskHandle_t start_task_handle; void start_task(void *pvParameters); /* 任务1配置 */ #define TASK1_STACK 128 #define TASK1_PRIORITY 2 TaskHandle_t task1_handle; void task1(void *pvParameters); /* 任务2配置 */ #define TASK2_STACK 128 #define TASK2_PRIORITY 3 TaskHandle_t task2_handle; void task2(void *pvParameters); /* 任务3配置 */ #define TASK3_STACK 128 #define TASK3_PRIORITY 4 TaskHandle_t task3_handle; void task3(void *pvParameters); /** * @description: 启动FreeRTOS */ void freertos_start(void) { /* 1. 创建起始任务(所有任务在起始任务中统一创建) */ xTaskCreate((TaskFunction_t)start_task, // 任务函数指针 (char *)"start_task", // 任务名称(用于调试标识) (configSTACK_DEPTH_TYPE)START_TASK_STACK, // 栈空间大小 128 word × 4 = 512 字节 (void *)NULL, // 传递给任务的参数 (UBaseType_t)START_TASK_PRIORITY, // 任务优先级 (TaskHandle_t *)&start_task_handle); // 返回任务句柄 /* 2. 启动调度器:将CPU控制权交给FreeRTOS */ vTaskStartScheduler(); } /** * @description: 起始任务函数,负责创建其他任务后自我删除 */ void start_task(void *pvParameters) { /* 进入临界区:防止创建任务过程中被中断干扰 */ taskENTER_CRITICAL(); /* 创建3个实际工作任务 */ xTaskCreate((TaskFunction_t)task1, (char *)"task1", (configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL, (UBaseType_t)TASK1_PRIORITY, (TaskHandle_t *)&task1_handle); xTaskCreate((TaskFunction_t)task2, (char *)"task2", (configSTACK_DEPTH_TYPE)TASK2_STACK, (void *)NULL, (UBaseType_t)TASK2_PRIORITY, (TaskHandle_t *)&task2_handle); xTaskCreate((TaskFunction_t)task3, (char *)"task3", (configSTACK_DEPTH_TYPE)TASK3_STACK, (void *)NULL, (UBaseType_t)TASK3_PRIORITY, (TaskHandle_t *)&task3_handle); /* 删除起始任务自身:传NULL代表删除当前运行的任务 */ vTaskDelete(NULL); /* 退出临界区 */ taskEXIT_CRITICAL(); } /** * @description: 任务1——翻转LED1,周期500ms */ void task1(void *pvParameters) { while (1) { printf("task1正在运行...\r\n"); LED_Toggle(LED1_Pin); // 翻转LED1 vTaskDelay(500); // 阻塞延时500ms,让出CPU } } /** * @description: 任务2——翻转LED2,周期500ms */ void task2(void *pvParameters) { while (1) { printf("task2正在运行...\r\n"); LED_Toggle(LED2_Pin); // 翻转LED2 vTaskDelay(500); // 阻塞延时500ms,让出CPU } } /** * @description: 任务3——检测按键,按下KEY1则删除task1 */ void task3(void *pvParameters) { uint8_t key = 0; while (1) { printf("task3正在运行...\r\n"); key = Key_Detect(); if (key == KEY1_PRESS) { /* 确保task1句柄有效 */ if (task1_handle != NULL) { /* KEY1按下则删除task1 */ printf("正在删除Task1....\r\n"); vTaskDelete(task1_handle); // 删除指定任务 task1_handle = NULL; // 句柄置空防止误用 } } vTaskDelay(500); // HAL_Delay(500); // 注意:HAL_Delay是忙等待,会占用CPU } } ``` #### P02:静态创建 **代码来源:**03_代码/02_静态创建任务和删除/Core/Src/freertos_demo.c ```c #include "freertos_demo.h" #include "FreeRTOS.h" #include "task.h" #include "LED.h" #include "Key.h" /* 任务栈和TCB变量全部由用户预先定义 */ #define START_TASK_STACK 128 StackType_t start_task_stack[START_TASK_STACK]; // 用户分配的栈空间 StaticTask_t start_task_tcb; // 用户分配的TCB空间 TaskHandle_t start_task_handle; #define TASK1_STACK 128 StackType_t task1_stack[TASK1_STACK]; StaticTask_t task1_tcb; TaskHandle_t task1_handle; #define TASK2_STACK 128 StackType_t task2_stack[TASK2_STACK]; StaticTask_t task2_tcb; TaskHandle_t task2_handle; #define TASK3_STACK 128 StackType_t task3_stack[TASK3_STACK]; StaticTask_t task3_tcb; TaskHandle_t task3_handle; /* ============= 系统任务内存分配(静态分配时必须提供)=============== */ StackType_t idle_task_stack[configMINIMAL_STACK_SIZE]; StaticTask_t idle_task_tcb; StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH]; StaticTask_t timer_task_tcb; /* 提供空闲任务的栈和TCB */ void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize) { *ppxIdleTaskTCBBuffer = &idle_task_tcb; *ppxIdleTaskStackBuffer = idle_task_stack; *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; } /* 提供定时器任务的栈和TCB */ void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) { *ppxTimerTaskTCBBuffer = &timer_task_tcb; *ppxTimerTaskStackBuffer = timer_task_stack; *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; } void freertos_start(void) { /* 静态创建起始任务:需要用户提供栈和TCB缓冲区 */ start_task_handle = xTaskCreateStatic( (TaskFunction_t)start_task, (char *)"start_task", (uint32_t)START_TASK_STACK, (void *)NULL, (UBaseType_t)START_TASK_PRIORITY, (StackType_t *)start_task_stack, // 用户预分配的栈 (StaticTask_t *)&start_task_tcb // 用户预分配的TCB ); vTaskStartScheduler(); } void start_task(void *pvParameters) { taskENTER_CRITICAL(); /* 静态创建3个工作任务 */ task1_handle = xTaskCreateStatic( (TaskFunction_t)task1, (char *)"task1", (configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL, (UBaseType_t)TASK1_PRIORITY, (StackType_t *)task1_stack, (StaticTask_t *)&task1_tcb ); task2_handle = xTaskCreateStatic( (TaskFunction_t)task2, (char *)"task2", (configSTACK_DEPTH_TYPE)TASK2_STACK, (void *)NULL, (UBaseType_t)TASK2_PRIORITY, (StackType_t *)task2_stack, (StaticTask_t *)&task2_tcb ); task3_handle = xTaskCreateStatic( (TaskFunction_t)task3, (char *)"task3", (configSTACK_DEPTH_TYPE)TASK3_STACK, (void *)NULL, (UBaseType_t)TASK3_PRIORITY, (StackType_t *)task3_stack, (StaticTask_t *)&task3_tcb ); vTaskDelete(NULL); taskEXIT_CRITICAL(); } /* task1/task2/task3 与动态创建版本相同 */ ``` ### 关键配置 **P01 FreeRTOSConfig.h**(默认配置,无需特殊设置): ```c // 默认配置已启用 vTaskDelete #define INCLUDE_vTaskDelete 1 ``` **P02 FreeRTOSConfig.h**(静态分配额外配置): ```c #define configSUPPORT_STATIC_ALLOCATION 1 // 启用静态分配 #define configUSE_TIMERS 1 // 启用软件定时器(产生定时器任务) #define configTIMER_TASK_PRIORITY 4 // 定时器任务优先级 #define configTIMER_QUEUE_LENGTH 5 // 定时器命令队列长度 #define configTIMER_TASK_STACK_DEPTH 256 // 定时器任务栈大小 ``` ## 核心函数速查表 | 函数 | 参数 | 返回值 | 功能简述 | |----------|-----------|--------|-------------| | `xTaskCreate` | pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask | BaseType_t | 动态创建任务 | | `xTaskCreateStatic` | pvTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, puxStackBuffer, pxTaskBuffer | TaskHandle_t | 静态创建任务 | | `vTaskDelete` | xTaskToDelete | void | 删除任务(NULL=自身) | | `vTaskStartScheduler` | 无 | void | 启动调度器 | | `vApplicationGetIdleTaskMemory` | ppxIdleTaskTCBBuffer, ppxIdleTaskStackBuffer, pulIdleTaskStackSize | void | 提供空闲任务内存(静态分配时) | | `vApplicationGetTimerTaskMemory` | ppxTimerTaskTCBBuffer, ppxTimerTaskStackBuffer, pulTimerTaskStackSize | void | 提供定时器任务内存(静态分配时) | ## 常见问题与避坑 1. **栈溢出**:栈大小设置不当会导致任务异常。调试时可设置 `configCHECK_FOR_STACK_OVERFLOW` 为 1 或 2,并实现 `vApplicationStackOverflowHook()`。 2. **删除后句柄未置空**:删除任务后其句柄仍指向原 TCB 地址,后续误用会导致崩溃。应在删除后立即将句柄置 `NULL`。 3. **动态创建失败**:堆空间不足时 `xTaskCreate` 返回 `errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY`。增大 `configTOTAL_HEAP_SIZE` 或改用静态创建。 4. **静态创建必须提供系统任务内存**:设置 `configSUPPORT_STATIC_ALLOCATION = 1` 后,必须实现 `vApplicationGetIdleTaskMemory`,若启用定时器还必须实现 `vApplicationGetTimerTaskMemory`。 5. **vTaskDelete(NULL) 后需要切换**:删除自身时,函数末尾会自动触发一次任务切换,因此无需手动调用 `taskYIELD()`。 6. **临界区内不要使用阻塞操作**:`taskENTER_CRITICAL()` / `taskEXIT_CRITICAL()` 之间不应调用 `vTaskDelay()` 等可能导致阻塞的 API。