时间片轮询的任务调度方法(一)

时间片轮询的任务调度方法(一)

使用场景

在MCU开发的时候肯定会碰到和时间有关的任务,例如:每10ms刷新屏幕数据,每20ms检测按键状态,每100ms读取传感器数据,电机每1分钟运行10s后关闭。

这些都是基于时间的任务,在未使用操作系统的时,有两种常见的做法:

1. 使用flag记录当前任务执行状态
(推荐指数:⭐)

基本思路为:在开启一个定时器中断中,作为时钟源,在中断中判断时间是否到达,时间到达后将任务标志为置位。然后在main函数的超级循环中检查标志位状态。

具体实现方式代码如下:

int read_senser_flag=0, check_key_flag=0;

//1ms 定时器中断
int timer_interrupt_1ms(void)
{
    static uint32_t sys_tick=0;

    sys_tick++;

    //检测时间是否到达10ms的整数倍,到达按键检测标志位置位
    if(sys_tick%20==0)
    {
        check_key_flag=1;
    }

    //检测时间是否到达100ms的整数倍,到达按键检测标志位置位
    if(sys_tick%100==0)
    {
        read_senser_flag=1;
    }
}

int main(void)
{
    while(1)
    {
		//检查标志位状态
        if(check_key_flag==1)
        {
            check_key_flag=0;   //清除运行标志位
            check_key();		//检查按键状态
        }

        if(read_senser_flag==1)
        {
            read_senser_flag=0  //清除运行标志位
            read_senser();		//读取传感器数据
        }
    }
}

使用该方式编写,思路非常简单,不过使用了过多的标志位,我称之为面象flag编程

对于小型简单的任务,使用该方式编写问题也不算太大。不过随着项目复杂度提高,标志位的数量急剧增长,对代码维护极不友好。

2. 使用系统滴答时钟判断任务执行状态
(推荐指数:⭐⭐)

实现思路:开启一个定时器,作为系统运行时钟。在main函数的超级循环中,检查当前时钟和上次执行时间的差值是否大于执行时间间隔,大于则执行任务。

具体实现方式代码如下:

#include <stdint.h>

//系统运行滴答时钟
uint32_t sys_tick=0;

//1ms 定时器中断
int timer_interrupt_1ms(void)
{
    sys_tick++;
}

//返回系统滴答时钟
uint32_t millis(void)
{
    return sys_tick;
}

int main(void)
{
    uint32_t last_read_senser_tick=0, last_check_key_tick=0;
    uint32_t last_run_motor_tick=0;

    while (1)
    {
        //检查当前时间和上一次执行时间的差值,超过20ms检查按键状态
        if(millis() - last_read_senser_tick > 20)
        {
            last_read_senser_tick=millis();
            read_senser();
        }

        //检查上一次执行时间和当前时间差值,超过100ms读取传感器数据
        if(millis() - last_check_key_tick > 100)
        {
            last_check_key_tick=millis();
            check_key();
        }

        //检查上一次电机执行时间和当前时间差值,超过60s,运行电机
        if(millis() - last_run_motor_tick > 60000)
        {
            last_check_key_tick=millis();
            run_motot();
        }

        //检查上一次电机执行时间和当前时间差值,超过10s,停止电机
        if(millis() - last_run_motor_tick > 10000)
        {
            stop_motot();
        }
    }
}

使用该方式对时间任务进行检查,减少flag的定义,并且可将时间判断语句定义到执行函数内部,减少全局变量的调用,优化代码结构。

该方式适用于少量定时任务的调度,当有大量的定时任务时,代码会变得重复而且臃肿。在每个定时任务的代码结构都是相同的,重复大量结构相同的代码并不是一件好的事情。

在上面的两种方法,都属于时间片轮询,不过没有形成稳定的框架,不过基本结构都是查询是否到达当前任务执行时间,然后依次执行任务

任务执行顺序如下图所示:

在这里插入图片描述

3. 基于时间片轮询调度算法
(推荐指数:⭐⭐)

在方法二的代码中,主要包含任务函数,上一次的执行时间、任务执行间隔以及执行时间查询,几部分。其中前三项为可变化的数据,第四项为固定的处理逻辑

对上述代码进行抽象和提取后,将可变化的数据使用数据结构封装,固定的处理逻辑写成方法,这就是一个简单的时间调度算法。

具体的时间调度算法的分析和实现,见下篇文章~


文中代码可在我的github 或者gittee 中查看,请多多star⭐

github 地址

gitee 地址

我开通微信公众号啦,更多及时的信息请关注公众号~
在这里插入图片描述

  • 6
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
时间片轮询是一种常见的实现多任务调度方法。它将 CPU 的时间片分配给多个任务,每个任务在一个时间段内运行一定的时间,在时间片结束之后,操作系统会切换到下一个任务,直至所有任务完成。以下是一个简单的时间片轮询例程的伪代码: ``` while (true) { for (i = 0; i < num_tasks; i++) { if (tasks[i].state == RUNNING) { // 如果当前任务正在运行 tasks[i].run_time++; // 增加运行时间 if (tasks[i].run_time >= time_slice) { // 如果运行时间达到时间片 tasks[i].state = READY; // 将任务状态设置为就绪 tasks[i].run_time = 0; // 重置运行时间 } } } for (i = 0; i < num_tasks; i++) { if (tasks[i].state == READY) { // 如果当前任务处于就绪状态 tasks[i].state = RUNNING; // 将任务状态设置为运行 run_task(&tasks[i]); // 运行任务 } } } ``` 在这个例程中,我们假设有一个任务列表 `tasks`,每个任务包含一个状态(运行、就绪、阻塞等)和已经运行的时间。在时间片轮询的过程中,我们逐个检查每个任务的状态,如果任务正在运行,就增加它的运行时间,并检查是否达到时间片的限制。如果是,则将任务状态设置为就绪,并将运行时间重置为 0。在第二个循环中,我们检查所有就绪的任务,并将它们的状态设置为运行,然后运行它们。 当然,实际的时间片轮询例程可能会更加复杂,需要考虑任务的优先级、阻塞和唤醒等情况,但是以上的伪代码可以作为一个基本的框架来理解时间片轮询的原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值