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(;;);
}
运行与观察方法
- 使用STM32CubeMX生成基础工程,使能FreeRTOS(CMSIS_V1或V2均可),配置USART1用于printf重定向。
- 将上述代码替换main.c中的任务创建部分,确保三个任务函数定义在main.c中。
- 编译烧录后,打开串口助手(波特率与CubeMX配置一致,通常115200),观察输出。
预期输出与分析
[HIGH] 运行中 (优先级3)
[HIGH] 运行中 (优先级3)
[MED ] 运行中 (优先级2)
[HIGH] 运行中 (优先级3)
[HIGH] 运行中 (优先级3)
[LOW ] 运行中 (优先级1)
...
判定标准:
- 高优先级任务(优先级3)每500ms打印一次,输出频率最高。
- 中优先级任务每1s打印一次,低优先级任务每2s打印一次。
- 当高优先级任务处于延时状态时,中/低优先级任务才能获得CPU;一旦高优先级任务延时结束,会立即抢占当前运行的低优先级任务。
- 若将高优先级任务的延时改为0(即不主动让出CPU),则中、低优先级任务将永远无法运行——这验证了抢占式调度的严格优先级特性。
关键说明
- 三个任务的延时时间不同,使得它们在不同时刻就绪,从而产生抢占行为。若所有任务延时相同,则只会按优先级顺序轮流执行,无法体现抢占。
- printf通过串口输出,实际打印耗时远小于任务延时,因此不会显著影响调度时序。
- 任务栈大小128字(512字节)足够本实验使用,实际工程中需根据任务内部变量和调用深度调整。