欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

时间片轮询法

程序员文章站 2022-12-22 22:53:44
时间片轮询法 时间片轮询法是一种比较简单易用的系统架构之一,它对于系统中的任务调度算法是分时处理。核心思路是把 CPU 的时间分时给各个任务使用。我们常用的定时方法是定时器,把调度器放在定时中,可以简单的实现时间片轮询法。 需要注意的是,这种方法的前提是执行的 每个任务都是短小精悍的 ,要不然一个任 ......

时间片轮询法

时间片轮询法是一种比较简单易用的系统架构之一,它对于系统中的任务调度算法是分时处理。核心思路是把 cpu 的时间分时给各个任务使用。我们常用的定时方法是定时器,把调度器放在定时中,可以简单的实现时间片轮询法。

需要注意的是,这种方法的前提是执行的 每个任务都是短小精悍的,要不然一个任务执行的时间过长,大于其它任务设置的时间片值,那其它任务就无法保证按它预设的时间片来执行。

尤其需要注意任务中延时的使用,可能会产生不可预料的结果。如果任务内部需要延时的时候,或者说单个任务过长,需要保存任务执行到一半的状态,建议使用状态机切割长任务。

时间片轮询法架构

一个时间片轮询应用程序的架构是非常简单的,包括一个任务结构体,一个中断处理函数,一个轮询执行任务函数。

设计一个结构体:

// 任务结构
typedef struct {
    uint8_t task_id;                  // 任务 id
    uint16_t task_interval;             // 任务运行间隔时间
    void (*task_entry)(void);           // 要运行的任务
    volatile uint16_t task_tick_ms;     // 计时器
}task_info_t;

定时器复用和中断处理

定时器可以是任意的定时器,这里采用系统滴答定时器 (systick) 来定时。systick 的配置就不细讲,假设定时器的定时中断为 10ms(可以自行设定,中断过于频繁效率就低,中断太长,实时性差)。

timing_task_tick 函数就相当于中断服务函数,需要在定时器的中断服务函数中调用此函数。

// 为每个任务计时,每次中断加 10ms
void timing_task_tick(void)
{
    uint8_t task_index = 0;

    while (task_index < array_size(timing_task_array))
    {
        timing_task_array[task_index].task_tick_ms += config_system_tick_period_ms;     // 每次加一个 systick 周期,即 10ms
        task_index++;
    }
}

时间片轮询实例

下面我就就说说怎样应用吧,假设我们有三个任务:时钟显示,按键扫描,和工作状态显示。

定义一个上面定义的那种结构体数组

// 计算任务个数
#define array_size(x) (sizeof(x) / sizeof((x)[0]))

// 定义了 3 个任务
static task_info_t timing_task_array[] =
{
    {0, 100, task_disp_clock_running, 0},    // 显示时钟
    {1, 20, task_scan_key_running, 0},      // 按键扫描
    {2, 30, task_disp_ws_running, 0},       // 工作状态显示
};

在定义变量时,我们已经初始化了值,这些值的初始化,非常重要,跟具体的执行时间优先级等都有关系,这个需要自己掌握。

  1. 大概意思是,我们有三个任务,每 1s 执行一下时钟显示,因为我们的时钟最小单位是 1s,所以在秒变化后才显示一次就够了。

  2. 由于按键在按下时会参数抖动,而我们知道一般按键的抖动大概是 20ms,那么我们在顺序执行的函数中一般是延伸 20ms,而这里我们每 20ms 扫描一次,是非常不错的出来,即达到了消抖的目的,也不会漏掉按键输入。

  3. 为了能够显示按键后的其他提示和工作界面,我们这里设计每 30ms 显示一次,如果你觉得反应慢了,你可以让这些值小一点。后面的名称是对应的函数名,你必须在应用程序中编写这函数名称和这三个一样的任务。

编写任务函数

//description : 显示任务
void task_disp_clock_running(void)
{

}

//description : 扫描任务
void task_scan_key_running(void)
{

}

//description : 工作状态显示
void task_disp_ws_running(void)
{

}

任务处理

// 任务计划表,轮询执行任务
void timing_task_scheduler(void)
{
    uint8_t task_index = 0;

    while (1)
    {
        for (task_index = 0 ; task_index < array_size(timing_task_array); task_index++)
        {
            if (timing_task_array[task_index].task_tick_ms >= timing_task_array[task_index].task_interval)
            {
                timing_task_array[task_index].task_tick_ms = 0;
                timing_task_array[task_index].task_entry();
            }
        }
    }
}

程序陷入死循环,依次判断每个任务是否符合执行要求。如果是,则执行相应的任务函数;否则等待计时。