int main(void)
{	
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  BSP_Init();
  printf("这是一个[野火]-STM32全系列开发板-FreeRTOS-动态创建任务!\r\n");
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数 */
                        (const char*    )"AppTaskCreate",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )1, /* 任务的优先级 */
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针 */          
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */
  else
    return -1;  
  
  while(1);   /* 正常不会执行到这里 */    
}


static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  taskENTER_CRITICAL();           //进入临界区
  xReturn = xTaskCreate((TaskFunction_t )LED_Task, /* 任务入口函数 */
                        (const char*    )"LED_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )2,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&LED_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建LED_Task任务成功!\r\n");
  
  
   xReturn = xTaskCreate((TaskFunction_t )LED2_Task, /* 任务入口函数 */
                        (const char*    )"LED2_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )2,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&LED2_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建LED2_Task任务成功!\r\n");
  
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  taskEXIT_CRITICAL();            //退出临界区
}

static void LED_Task(void* parameter)
{	
    while (1)
    {
        LED1_ON;
        vTaskDelay(500);   /* 延时500个tick */
        printf("LED_Task Running,LED1_ON\r\n");
        
        LED1_OFF;     
        vTaskDelay(500);   /* 延时500个tick */		 		
        printf("LED_Task Running,LED1_OFF\r\n");
    }
}

static void LED2_Task(void* parameter)
{	
    while (1)
    {
        LED2_ON;
        vTaskDelay(1000);   /* 延时500个tick */
        printf("LED_Task Running,LED2_ON\r\n");
        
        LED2_OFF;     
        vTaskDelay(1000);   /* 延时500个tick */		 		
        printf("LED_Task Running,LED2_OFF\r\n");
    }
}

一、Main 函数阶段:任务注册(未执行任务逻辑)

main 函数是程序的入口,此时 FreeRTOS 调度器未启动,CPU 完全由 main 函数掌控,执行顺序如下:

  1. 初始化返回值变量:定义xReturn并赋值为pdPASS,用于接收任务创建的返回状态。
  2. 硬件初始化:调用BSP_Init()完成开发板底层硬件初始化(如 GPIO、串口、时钟等),为后续任务运行提供硬件支持。
  3. 打印提示信息:通过printf输出日志,此时串口已在BSP_Init()中初始化完成。
  4. 注册 AppTaskCreate 任务:调用xTaskCreate函数,该函数的作用是将 AppTaskCreate 任务的信息注册到 FreeRTOS 内核的任务列表中,并非立即执行任务逻辑:
    • 内核会为该任务分配栈空间、初始化任务控制块(TCB),并将任务加入 “就绪队列”(此时任务处于 “就绪态”,等待调度)。
    • 此时 CPU 仍在 main 函数中,AppTaskCreate 的任务逻辑(函数体内部代码)完全未执行。
  5. 判断任务创建结果:若xReturnpdPASS(说明 AppTaskCreate 任务注册成功),则调用vTaskStartScheduler();否则返回 - 1,程序异常退出。
  6. 启动任务调度器(关键步骤)vTaskStartScheduler()是 FreeRTOS 的核心函数,执行以下操作:
    • 初始化调度器相关内核资源(如就绪列表、定时器等)。
    • 根据任务优先级,从就绪队列中选出最高优先级的就绪任务(此时仅有 AppTaskCreate 任务,优先级为 1)。
    • 执行上下文切换:保存 main 函数的运行上下文(寄存器、栈指针等),恢复 AppTaskCreate 任务的上下文,将 CPU 控制权交给该任务。
    • 至此,AppTaskCreate 任务才开始真正执行(而非创建后直接执行)。
  7. main 函数的死循环vTaskStartScheduler()启动调度器后,若调度器正常运行,永远不会返回(后续 CPU 被任务调度器管理),因此while(1)不会被执行。

二、AppTaskCreate 任务阶段:创建业务任务并自我删除

当调度器完成上下文切换后,CPU 开始执行 AppTaskCreate 函数的逻辑,执行顺序如下:

  1. 初始化 LED 任务创建返回值:定义xReturn并赋值为pdPASS,用于接收 LED_Task 和 LED2_Task 的创建状态。
  2. 进入临界区(保证任务创建原子性):调用taskENTER_CRITICAL(),作用是关闭全局中断(或禁止任务调度,取决于 FreeRTOS 配置),防止在创建 LED 任务的过程中,被中断或其他任务(此时暂无其他任务)打断,确保两个 LED 任务的创建过程完整、有序执行。
  3. 创建 LED_Task 任务
    • 调用xTaskCreate注册 LED_Task 任务,配置任务栈 512 字节、优先级 2、任务句柄LED_Task_Handle,无入口参数。
    • 若创建成功(xReturn == pdPASS),打印 “创建 LED_Task 任务成功!”,此时 LED_Task 进入 “就绪态”,但因处于临界区,调度器被禁止,无法执行。
  4. 创建 LED2_Task 任务
    • 同理,调用xTaskCreate注册 LED2_Task 任务,配置参数与 LED_Task 一致(栈 512 字节、优先级 2、句柄LED2_Task_Handle)。
    • 若创建成功,打印 “创建 LED2_Task 任务成功!”,LED2_Task 同样进入 “就绪态”,等待调度。
  5. 自我删除 AppTaskCreate 任务:调用vTaskDelete(AppTaskCreate_Handle)将自身任务标记为 “待删除态”:
    • 该任务不会立即终止,会继续执行后续代码(退出临界区),其资源(栈、TCB)会由 FreeRTOS 空闲任务后续清理。
    • 此时 AppTaskCreate 的使命(创建业务任务)已完成,自我删除可释放系统资源,避免无效占用。
  6. 退出临界区(恢复任务调度):调用taskEXIT_CRITICAL(),恢复全局中断和任务调度功能,此时调度器立即生效:
    • 就绪队列中存在两个高优先级任务(LED_Task、LED2_Task,优先级 2,高于 AppTaskCreate 的优先级 1),调度器会从这两个同优先级任务中选择一个,执行上下文切换,将 CPU 控制权交给它。
    • 至此,AppTaskCreate 任务完成所有逻辑,后续由空闲任务清理其资源,不再参与调度。
为何 AppTaskCreate 任务被删除后,会切换到 LED_Task 和 LED2_Task?

        在 AppTaskCreate 任务中,创建 LED_Task 和 LED2_Task 的过程,早已为 “切换到这两个任务” 做好了准备:

  1. 当调用xTaskCreate创建 LED_Task 和 LED2_Task 时,FreeRTOS 内核已经完成了这两个任务的注册:分配栈空间、初始化 TCB、并将它们加入到「就绪队列」中,此时这两个任务处于 **“就绪态”**(具备执行条件,只等待 CPU 控制权)。
  2. 之所以这两个高优先级任务没有立即执行,仅仅是因为 AppTaskCreate 任务调用了taskENTER_CRITICAL()进入了临界区 —— 临界区会禁止任务调度(阻断上下文切换),即便有更高优先级的就绪任务,调度器也无法切换 CPU 控制权,只能等待临界区退出。

三、最终阶段:LED_Task 与 LED2_Task 循环执行

调度器切换到 LED_Task/LED2_Task 后,两个任务按 FreeRTOS 调度策略运行(默认抢占式 + 时间片轮转):

  1. 若任务是循环逻辑(通常 LED 任务为 “延时 - 翻转 LED - 再延时” 的循环),则会持续运行,周期性执行 LED 控制操作。
  2. 同优先级的两个任务会交替获得 CPU 控制权,实现 “并发” 执行效果(宏观上同时运行,微观上分时切换)。

四、FreeRTOS 空闲任务(Idle Task)的基础定位

当你调用vTaskStartScheduler()启动调度器时,FreeRTOS 会自动创建一个最低优先级(优先级为 0)的空闲任务,它是系统必有的系统任务,具有两个核心作用:

  1. 当系统中没有其他更高优先级的就绪任务时,CPU 会执行空闲任务(避免 CPU 处于 “无任务可执行” 的空转状态)。
  2. 负责清理被vTaskDelete()标记为 “待删除态” 的任务资源 

五、待清理的资源:被删除任务的两大核心资源

代码中,vTaskDelete(AppTaskCreate_Handle)只是将 AppTaskCreate 任务标记为 “待删除态”,并未立即释放资源,这些待清理的资源主要有两类:

1. 任务栈(Task Stack)

  • 每个任务创建时(xTaskCreate动态创建),FreeRTOS 会从堆空间中为任务分配指定大小的栈空间(你的代码中是 512 字节)。
  • 任务栈的作用:存储任务运行时的临时数据(局部变量、函数调用参数)、上下文切换时的寄存器值、返回地址等,是任务运行的 “临时内存空间”。
  • 当任务被标记为待删除后,该栈空间不会立即释放,若长期不清理,会造成堆内存泄漏,后续无法为新任务分配栈空间。

2. 任务控制块(Task Control Block,TCB)

  • TCB 是每个任务的 “身份信息与管理载体”,是一个结构体,包含了任务的优先级、任务状态(就绪 / 阻塞 / 挂起)、栈指针、任务句柄、任务名称等核心管理信息。
  • 动态创建任务时,TCB 同样从堆空间中分配内存,它是 FreeRTOS 内核管理任务的 “核心数据结构”,被删除后若不回收,会持续占用堆内存,且占用内核任务列表的无效节点。

除此之外,若任务创建时还有其他动态分配的附属资源(如任务私有缓冲区),空闲任务不会自动清理(需用户在任务删除前手动释放),但上述栈和 TCB 是空闲任务的默认清理对象。

六、清理的时机与具体过程

1. 清理时机:仅当无更高优先级任务就绪时

空闲任务是最低优先级(0),遵循 “高优先级任务优先执行” 的调度规则,只有满足以下条件时,空闲任务才会获得 CPU 控制权,执行资源清理:

  • 系统中所有高优先级任务(你的代码中是 LED_Task、LED2_Task,优先级 2)都处于 “阻塞态”(如调用vTaskDelay()延时)或 “挂起态”,没有就绪任务可执行。
  • 例如:当 LED_Task 和 LED2_Task 都在执行vTaskDelay()进入阻塞态时,CPU 无业务任务可执行,此时调度器会切换到空闲任务,空闲任务便会执行资源清理工作。

2. 具体清理过程

  1. 空闲任务运行时,会遍历 FreeRTOS 内核的 “待删除任务列表”(所有被vTaskDelete()标记的任务都会进入该列表)。
  2. 对于列表中的每个待删除任务,首先释放其占用的任务栈内存(将栈空间归还给堆管理器,供后续新任务创建使用)。
  3. 接着释放任务 TCB 的内存(将 TCB 结构体占用的堆空间回收,同时将该任务从内核的所有管理列表(就绪列表、阻塞列表等)中移除,标记为无效任务)。
  4. 清理完成后,将该任务从 “待删除任务列表” 中移除,至此,被删除任务的所有默认资源已完全释放,不会造成内存泄漏。
  5. 若待删除任务列表为空,空闲任务会进入 “空循环”(轻量级空转,尽量减少 CPU 占用),直到有高优先级任务就绪,调度器会立即切换 CPU 控制权。

七、AppTaskCreate 任务的资源清理流程

  1. AppTaskCreate 任务调用vTaskDelete(AppTaskCreate_Handle)后,自身被标记为 “待删除态”,进入待删除任务列表,此时它的栈和 TCB 仍占用堆内存。
  2. AppTaskCreate 任务执行完taskEXIT_CRITICAL()后,调度器切换到 LED_Task/LED2_Task(优先级 2 更高),这两个任务正常运行,空闲任务处于就绪态等待。
  3. 当 LED_Task 执行vTaskDelay(500)、LED2_Task 执行vTaskDelay(1000)时,两个任务均进入阻塞态,系统中无就绪的高优先级任务。
  4. 调度器将 CPU 控制权交给空闲任务(优先级 0),空闲任务遍历待删除任务列表,找到 AppTaskCreate 任务。
  5. 空闲任务依次释放 AppTaskCreate 任务的 512 字节栈空间和 TCB 内存,将这些内存归还给堆管理器。
  6. 清理完成后,AppTaskCreate 任务从所有内核列表中移除,后续不再参与任何调度,其资源可被新任务(若有)复用。

总结

  1. 空闲任务是 FreeRTOS 自动创建的最低优先级任务,资源清理是其核心职责之一。
  2. 清理的核心资源是被删除任务的动态分配任务栈TCB 结构体,避免堆内存泄漏。
  3. 清理时机是 “无更高优先级任务就绪时”,不会影响业务任务的正常执行。
  4. 你的代码中,AppTaskCreate 任务的资源会在 LED 任务阻塞期间,由空闲任务自动回收,无需手动干预。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐