3. 时间片轮转调度

时间片轮转调度 学习卡片

任务定义

在RTOS中,当多个任务具有相同优先级时,需要一种机制让它们公平共享CPU。本案例基于FreeRTOS,创建两个同优先级任务,通过配置时间片轮转调度(Round-Robin),使两个任务按固定时间片交替执行。最终通过串口输出的任务执行序列,验证时间片轮转是否生效。

实现原理

FreeRTOS的调度器在以下两个条件同时满足时启用时间片轮转:① 宏configUSE_TIME_SLICING定义为1;② 多个任务处于同一优先级且均为就绪态。此时,每个任务运行一个时间片(由configTICK_RATE_HZ决定,默认1ms)后,调度器强制切换到同优先级的另一个就绪任务。若只有一个同优先级任务就绪,则时间片轮转不产生实际切换。

代码实现

// main.c
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

// 串口发送字符串(硬件相关,需根据平台实现)
extern void UART_SendString(const char *str);

// 两个同优先级任务:打印任务名后延时
void vTaskA(void *pvParameters) {
    for (;;) {
        UART_SendString("A\r\n");
        vTaskDelay(2);  // 主动让出CPU,但时间片轮转仍会生效
    }
}

void vTaskB(void *pvParameters) {
    for (;;) {
        UART_SendString("B\r\n");
        vTaskDelay(2);
    }
}

int main(void) {
    // 硬件初始化(时钟、UART等,略)
    
    // 创建两个同优先级任务(优先级均为1)
    xTaskCreate(vTaskA, "TaskA", 128, NULL, 1, NULL);
    xTaskCreate(vTaskB, "TaskB", 128, NULL, 1, NULL);
    
    // 启动调度器
    vTaskStartScheduler();
    
    // 正常不会执行到这里
    for (;;);
    return 0;
}

FreeRTOSConfig.h关键配置:

#define configUSE_PREEMPTION    1       // 抢占式调度
#define configUSE_TIME_SLICING  1       // 启用时间片轮转
#define configTICK_RATE_HZ      1000    // 时钟节拍1kHz,时间片=1ms
#define configMAX_PRIORITIES    5

时间片轮转执行时序

时间片轮转调度时序(优先级相同,时间片=1ms) 0ms 1ms 2ms 3ms 4ms 5ms 任务A 任务B 任务A 任务B 任务A 任务A执行 任务B执行 调度切换点 注:vTaskDelay(2)使任务主动阻塞2个节拍,但时间片轮转仍按1ms节拍强制切换

落地与验证

运行方式:将代码编译后烧录至目标板(如STM32F407),打开串口调试助手,波特率与UART_SendString实现一致(通常115200)。

观察现象:串口持续交替输出A和B,且输出序列呈现周期性交替,例如:

A
B
A
B
A
B
...

判定标准:

  1. 若串口输出为连续的A或B(长时间无交替),说明时间片轮转未生效——检查configUSE_TIME_SLICING是否为1,以及两个任务优先级是否确实相同。
  2. 若输出呈现A、B交替,但间隔不规律,可能原因是任务中vTaskDelay(2)导致任务主动阻塞,掩盖了时间片切换。可去掉vTaskDelay,改为空循环(如for(;;);),此时时间片轮转效果更明显——两个任务会严格按1ms时间片交替。
  3. 若将其中一个任务优先级改为2(高于另一个),则高优先级任务独占CPU,串口只输出高优先级任务内容,这反向验证了同优先级是时间片轮转的前提。

验证要点:本案例核心是验证configUSE_TIME_SLICING=1时,同优先级任务按时间片自动切换。若需更精确观测,可在任务切换点翻转GPIO并用逻辑分析仪测量,但串口输出已足以定性判断轮转是否生效。