2. 任务优先级实验

任务目标

在FreeRTOS中创建三个不同优先级的任务,通过串口输出观察高优先级任务如何抢占低优先级任务的CPU执行权,直观理解基于优先级的抢占式调度机制。

核心原理

FreeRTOS是抢占式实时内核。当更高优先级的任务进入就绪态时(例如延时到期或事件触发),调度器会立即暂停当前运行的低优先级任务,将CPU切换给高优先级任务。本实验通过三个任务以不同周期打印运行标记,展示这一抢占过程。

完整代码

/* FreeRTOS优先级抢占实验 - 适用于STM32F407 + HAL库 */
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"

/* 任务函数声明 */
void vHighPriorityTask(void *pvParameters);
void vMediumPriorityTask(void *pvParameters);
void vLowPriorityTask(void *pvParameters);

/* 任务句柄 */
TaskHandle_t xHighTask, xMediumTask, xLowTask;

void vTask1(void *pvParameters)  /* 高优先级任务:优先级3 */
{
    for(;;)
    {
        printf("[HIGH] 运行中 (优先级3)\r\n");
        vTaskDelay(pdMS_TO_TICKS(500));   /* 延时500ms,让出CPU */
    }
}

void vTask2(void *pvParameters)  /* 中优先级任务:优先级2 */
{
    for(;;)
    {
        printf("[MED ] 运行中 (优先级2)\r\n");
        vTaskDelay(pdMS_TO_TICKS(1000));  /* 延时1s */
    }
}

void vTask3(void *pvParameters)  /* 低优先级任务:优先级1 */
{
    for(;;)
    {
        printf("[LOW ] 运行中 (优先级1)\r\n");
        vTaskDelay(pdMS_TO_TICKS(2000));  /* 延时2s */
    }
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();   /* 系统时钟配置,由CubeMX生成 */
    MX_USART1_UART_Init();  /* 串口初始化,用于printf输出 */

    /* 创建三个不同优先级的任务 */
    xTaskCreate(vTask1, "High",  128, NULL, 3, &xHighTask);
    xTaskCreate(vTask2, "Medium",128, NULL, 2, &xMediumTask);
    xTaskCreate(vTask3, "Low",   128, NULL, 1, &xLowTask);

    /* 启动调度器 */
    vTaskStartScheduler();

    /* 正常情况下不会运行到这里 */
    for(;;);
}

运行与观察方法

  1. 使用STM32CubeMX生成基础工程,使能FreeRTOS(CMSIS_V1或V2均可),配置USART1用于printf重定向。
  2. 将上述代码替换main.c中的任务创建部分,确保三个任务函数定义在main.c中。
  3. 编译烧录后,打开串口助手(波特率与CubeMX配置一致,通常115200),观察输出。

预期输出与分析

[HIGH] 运行中 (优先级3)
[HIGH] 运行中 (优先级3)
[MED ] 运行中 (优先级2)
[HIGH] 运行中 (优先级3)
[HIGH] 运行中 (优先级3)
[LOW ] 运行中 (优先级1)
...

判定标准:

  • 高优先级任务(优先级3)每500ms打印一次,输出频率最高。
  • 中优先级任务每1s打印一次,低优先级任务每2s打印一次。
  • 当高优先级任务处于延时状态时,中/低优先级任务才能获得CPU;一旦高优先级任务延时结束,会立即抢占当前运行的低优先级任务。
  • 若将高优先级任务的延时改为0(即不主动让出CPU),则中、低优先级任务将永远无法运行——这验证了抢占式调度的严格优先级特性。

关键说明

  • 三个任务的延时时间不同,使得它们在不同时刻就绪,从而产生抢占行为。若所有任务延时相同,则只会按优先级顺序轮流执行,无法体现抢占。
  • printf通过串口输出,实际打印耗时远小于任务延时,因此不会显著影响调度时序。
  • 任务栈大小128字(512字节)足够本实验使用,实际工程中需根据任务内部变量和调用深度调整。