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

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

使用场景

在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 地址

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值