单片机:实现简单的时间片轮询调度(附带源码)

单片机:实现简单的时间片轮询调度

1. 项目背景与目标

在嵌入式系统中,时间片轮询(Round Robin Scheduling)是一种常用的多任务调度算法。通过将可执行的任务按照时间片进行轮流调度,可以使得系统在多个任务之间进行切换,保证每个任务都能够得到足够的处理时间,避免某些任务一直占用CPU导致其他任务无法得到执行。

本项目的目标是基于单片机实现一个简单的时间片轮询调度系统,能够在不同任务之间进行切换,实现一个多任务的基本调度模型。

2. 硬件设计
2.1 硬件组件
  1. 单片机:如STM32、8051、AVR等,本项目假设使用STM32单片机。
  2. 外设设备:如LED、蜂鸣器、按键、LCD显示等,用于展示调度结果和测试不同任务的执行。
  3. 定时器:使用定时器中断机制来触发时间片轮询调度。
2.2 硬件连接
  1. LED或蜂鸣器:用于显示任务的切换或执行结果。可以通过GPIO连接STM32的不同引脚。
  2. 按键:模拟任务的输入,可以用来触发任务的执行或调度。
  3. LCD显示器(可选):用于显示当前执行任务的信息,可以通过I2C或SPI与STM32连接。
3. 软件设计
3.1 时间片轮询调度原理

时间片轮询调度的核心思想是将CPU的时间划分为多个时间片,每个任务被分配一个时间片。系统按照固定的时间片顺序执行任务,每当时间片用完,系统便会切换到下一个任务。简单来说,它通过定时器中断来触发任务切换。

3.2 调度策略
  1. 任务划分:将任务划分为多个时间片,每个任务在其时间片内执行。
  2. 任务调度:使用定时器中断来定时触发任务的调度,模拟任务切换。
  3. 任务执行:每个任务可以是简单的LED闪烁、蜂鸣器响声等,用来测试调度系统的切换能力。
3.3 程序设计思路
  1. 任务管理:将不同的任务放入一个任务队列,每个任务有一个固定的执行周期(时间片)。在时间片到期后,系统会切换到下一个任务。
  2. 时间片控制:使用定时器中断来定时触发任务调度,每个任务占用一个时间片。
  3. 任务调度:当一个任务的时间片用尽后,系统自动调度下一个任务,按照轮询的方式执行任务。
3.4 代码实现

以下是基于STM32单片机实现简单时间片轮询调度的代码示例:

#include "stm32f4xx_hal.h"

// 定义LED引脚
#define LED_PIN     GPIO_PIN_5
#define LED_PORT    GPIOA

// 定义任务数量
#define MAX_TASKS   3

// 定义每个任务的时间片
#define TIME_SLICE   100 // 每个任务的时间片,单位:毫秒

// 任务控制结构体
typedef struct {
    void (*task_func)(void);  // 任务函数指针
    uint32_t time_slice;      // 时间片
} Task_t;

// 任务队列
Task_t task_queue[MAX_TASKS];

// 当前执行的任务索引
uint8_t current_task = 0;

// 定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    // 切换到下一个任务
    current_task++;
    if (current_task >= MAX_TASKS) {
        current_task = 0;
    }
}

// 任务1:LED闪烁
void Task1(void) {
    HAL_GPIO_TogglePin(LED_PORT, LED_PIN);  // 切换LED状态
}

// 任务2:控制蜂鸣器
void Task2(void) {
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);  // 切换蜂鸣器状态
}

// 任务3:显示当前任务
void Task3(void) {
    printf("Executing Task %d\n", current_task);  // 输出当前任务
}

// 初始化任务队列
void Task_Init(void) {
    task_queue[0].task_func = Task1;
    task_queue[0].time_slice = TIME_SLICE;
    
    task_queue[1].task_func = Task2;
    task_queue[1].time_slice = TIME_SLICE;
    
    task_queue[2].task_func = Task3;
    task_queue[2].time_slice = TIME_SLICE;
}

// 初始化定时器
void Timer_Init(void) {
    TIM_HandleTypeDef htim;
    __HAL_RCC_TIM2_CLK_ENABLE();
    
    htim.Instance = TIM2;
    htim.Init.Prescaler = 8399;  // 设定定时器预分频
    htim.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim.Init.Period = 999;  // 定时器周期为100ms
    htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&htim);
    
    HAL_TIM_Base_Start_IT(&htim);  // 启动定时器并使能中断
}

// 主程序
int main(void) {
    HAL_Init();  // 初始化HAL库
    
    // 初始化LED引脚
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = LED_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);

    // 初始化蜂鸣器引脚(假设连接在GPIOB的PIN0)
    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    // 初始化任务
    Task_Init();
    
    // 初始化定时器
    Timer_Init();
    
    while (1) {
        // 执行当前任务
        task_queue[current_task].task_func();
        HAL_Delay(TIME_SLICE);  // 每个任务执行完后,等待时间片结束
    }
}
3.5 代码解释
  1. 任务管理

  2. Task_t结构体用于管理每个任务的函数指针和时间片。每个任务函数指针指向一个具体的任务函数,time_slice表示任务的时间片。
  3. task_queue[MAX_TASKS]是任务队列,存储所有任务及其执行信息。
  4. 任务函数

  5. Task1():通过控制LED引脚的状态,模拟一个任务的执行。
  6. Task2():通过控制蜂鸣器引脚的状态,模拟另一个任务的执行。
  7. Task3():输出当前任务信息,模拟第三个任务的执行。
  8. 定时器中断

  9. 使用STM32的定时器2(TIM2)来创建一个周期性的中断,每100ms触发一次。定时器中断回调函数HAL_TIM_PeriodElapsedCallback()负责切换任务,模拟时间片轮询。
  10. 任务切换

  11. 在主循环中,程序会按照当前任务的时间片执行任务。每次执行完一个任务后,等待TIME_SLICE的时间(通过HAL_Delay()实现),然后切换到下一个任务。
  12. 任务调度

  13. current_task记录当前执行的任务索引,每当一个任务执行完一个时间片后,current_task会增加1,指向下一个任务。如果任务索引超过最大任务数,则从头开始调度。
4. 总结

本项目实现了基于STM32单片机的简单时间片轮询调度系统,通过定时器中断周期性地切换任务,模拟了一个简单的多任务调度。每个任务有一个固定的时间片,任务会轮流执行。系统的时间片可以通过调整TIME_SLICE的值来改变。虽然这只是一个简单的示范,但为实现更加复杂的调度器奠定了基础。在实际应用中,调度算法可以根据不同的需求进行优化,支持优先级调度、任务挂起、任务切换等功能。

作者:Katie。

物联沃分享整理
物联沃-IOTWORD物联网 » 单片机:实现简单的时间片轮询调度(附带源码)

发表回复