单片机简易时间片轮询结构设计的工作经验总结

目录

一、单片机中常见的几种模式介绍

二、简易时间片轮询结构设计

1、任务调度表的设计

(1)任务调度表的结构体设计

(2)时间片的处理与任务的启停

(3)任务调度表的实现

三、关于上述时间片轮询结构设计的一些补充

1、任务调度表的优先级调度设计

(1)任务调度表优先级与优先级计算的设计

(2)时间片轮询处理函数的修改

(3)任务调度函数的修改


一、单片机中常见的几种模式介绍

在MCU的设计与开发中有以下常见的程序结构设计:

  • 裸机:功能单一,简单的顺序执行
  • 时间片轮询:多任务、内存占用较少
  • RTOS:多任务、系统复杂性高,有较高的实时性要求和可靠性要求
  • 其实一般情况下能上RTOS一般就上RTOS,根本不需要考虑时间片轮询的情况,斟酌使用时间片轮询的情况主要的考虑有以下几点:

  • MCU的RAM和ROM资源问题,引入RTOS会带来额外的内存开销
  • 业务功能的拆解与多任务的设计,有时候使用时间片轮询可以让整个结构更加简单易懂,方便设计
  • 调试更为简单方便
  • 使用时间片轮询时要注意的点:

  • 功能任务划分的合理性,多任务的情况下,每个任务的执行都尽量设计成短小精悍。(例如:执行1个任务需要10ms,你不能只给它分配5ms的时间片)
  • 任务数量与MCU主频的简单关系估算:任务数量 = MCU主频 / (时间片大小 × 任务平均处理时间) ;例如在一个系统中有10个任务,并且每个任务需要相同的时间片大小为10ms,那么每个任务将获得1/10的CPU时间,也就是10%的CPU时间。如果我们希望系统总体运行效率高达80%,那么MCU的主频应该是所有任务的最小执行时间的8倍,即主频应该为1/(10毫秒* 8) = 1250 Hz
  • 二、简易时间片轮询结构设计

    1、任务调度表的设计

    (1)任务调度表的结构体设计

    其中,任务调度表中包含了任务编号(或者可以设计成表中的索引)回调的任务函数任务类型(周期或者事件类型)任务开关、任务状态(包括执行后的状态)、任务时间片计数、任务时间片初始值

    /******************************************
     *          OS Task No
     ******************************************/
    #define OS_TASK_NO1   0
    #define OS_TASK_NO2   1
    #define OS_TASK_NO3   2
    #define OS_TASK_NO4   3
    #define OS_TASK_NO5   4
    #define OS_TASK_NO6   5
    #define OS_TASK_NO7   6
    #define OS_TASK_NO8   7
    
    #define OS_TASK_MAX_NO 8
    
    /******************************************
     *          OS Task switch
     ******************************************/
    #define OS_TASK_OFF       0
    #define OS_TASK_ON        1
    
    /******************************************
     *          OS Task Type
     ******************************************/
    #define OS_TASK_EVENT     0
    #define OS_TASK_CYCLC     1
    
    /******************************************
     *          OS Task Status
     ******************************************/
    #define OS_TASK_OK             0
    #define OS_TASK_READY          1
    #define OS_TASK_ERROR          2
    
    /******************************************
     *          Struct and Enum
     ******************************************/
    typedef struct 
    {
        uint8_t os_task_number;
        uint8_t (*taskFunc)(void);
        uint8_t os_task_type;
        uint8_t os_task_switch;
        uint8_t os_task_status;
        uint8_t os_task_tickCnt;
        uint8_t os_task_tickInit;
    
    }OS_TASK_SCHEDULE_t;

    (2)时间片的处理与任务的启停

  • 时间片的处理:放置在定时器或者Systick定时器的中断中,对所有任务的时间片计数进行处理,当计数值减少到0时,把对应任务的状态赋为Ready态,并对时间片赋初始计数值
  • void OS_TmrIsrTask(void)
    {
        uint8_t indx = 0;
    
        OS_DISABLE_INTERRUPT();
        for(indx = 0; indx < osTaskScheduleCfgSize; indx++)
        {
            if(osTaskScheduleCfg[indx].os_task_switch == OS_TASK_ON &&\
               osTaskScheduleCfg[indx].os_task_status != OS_TASK_READY)
            {
                if(osTaskScheduleCfg[indx].os_task_tickCnt > 0)
                {
                    osTaskScheduleCfg[indx].os_task_tickCnt--;
                }
    
                if(osTaskScheduleCfg[indx].os_task_tickCnt == 0)
                {
                    osTaskScheduleCfg[indx].os_task_status = OS_TASK_READY;
                    
                    switch(osTaskScheduleCfg[indx].os_task_type)
                    {
                        case OS_TASK_CYCLC:
                        {
                            osTaskScheduleCfg[indx].os_task_tickCnt = osTaskScheduleCfg[indx].os_task_tickInit;
                            break;
                        }
                        case OS_TASK_EVENT:
                        {
                            break;
                        }
                    }
    
                }
    
            }
        }
        OS_ENABLE_INTERRUPT();
    }
  • 任务的启停

  • void OS_TaskTmrEventStart(uint8_t task_number)
    {
        uint8_t indx = 0;
    
        OS_DISABLE_INTERRUPT();
        for(indx = 0; indx <= osTaskScheduleCfgSize; indx++)
        {
            if(task_number == osTaskScheduleCfg[indx].os_task_number)
            {
                osTaskScheduleCfg[indx].os_task_switch = OS_TASK_ON;
                osTaskScheduleCfg[indx].os_task_tickCnt = osTaskScheduleCfg[indx].os_task_tickInit;
    
                break;
            }
        }
        OS_ENABLE_INTERRUPT();
        
    }
    
    void OS_TaskTmrEventStop(uint8_t task_number)
    {
        uint8_t indx = 0;
    
        OS_DISABLE_INTERRUPT();
        for(indx = 0; indx <= osTaskScheduleCfgSize; indx++)
        {
            if(task_number == osTaskScheduleCfg[indx].os_task_number)
            {
                osTaskScheduleCfg[indx].os_task_switch = OS_TASK_OFF;
                osTaskScheduleCfg[indx].os_task_tickCnt = 0;
    
                break;
            }
        }
        OS_ENABLE_INTERRUPT();
    }

    (3)任务调度表的实现

  • 任务调度表的实现:循环遍历任务调度表中每个任务的状态,当有处于Ready状态的任务时,则去执行。
  • void OS_StartSchedule(void)
    {
        uint8_t indx;
    
        for(;;)
        {
            for(indx = 0; indx < osTaskScheduleCfgSize; indx++)
            {
                switch(osTaskScheduleCfg[indx].os_task_status)
                {
                    case OS_TASK_OK:
                    {
                        break;
                    }
                    case OS_TASK_ERROR:
                    {
                        break;
                    }
                    case OS_TASK_READY:
                    {
                        OS_DISABLE_INTERRUPT();
                        if(osTaskScheduleCfg[indx].taskFunc != NULL)
                        {
                            osTaskScheduleCfg[indx].os_task_status = osTaskScheduleCfg[indx].taskFunc();
    
                            if(osTaskScheduleCfg[indx].os_task_type == OS_TASK_EVENT)
                            {
                                osTaskScheduleCfg[indx].os_task_switch = OS_TASK_OFF;
                            }
    
                        }
                        OS_ENABLE_INTERRUPT();
                        break;
                    }
                }
            }
        }   
    }
  • 任务调度表与任务的配置:其中还可以根据对初始化tickCnt的值的差异,来控制先后执行任务的顺序
  • /*********************************************
     *          OS Task Schedule Config 
     *********************************************/
    OS_TASK_SCHEDULE_t osTaskScheduleCfg[] = 
    {   
        /* level            taskFunc     task_type           task_switch      os_task_status     task_tickCnt    task_tickInit */
        {OS_TASK_NO1,       os_task1,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        0,              100               },
        {OS_TASK_NO2,       os_task2,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        1,              100               },
        {OS_TASK_NO3,       os_task3,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        2,              100               },
        {OS_TASK_NO4,       os_task4,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        3,              100               },
        {OS_TASK_NO5,       os_task5,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        4,              100               },
        {OS_TASK_NO6,       os_task6,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        5,              100               },
        {OS_TASK_NO7,       os_task7,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        6,              100               },
        {OS_TASK_NO8,       os_task8,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        7,              100               },
    
    };
    
    uint8_t osTaskScheduleCfgSize = sizeof(osTaskScheduleCfg) / sizeof(OS_TASK_SCHEDULE_t);
    
    /******************************************
     *          User Task Function
     ******************************************/
    uint8_t os_task1(void)
    {
        /* do something */
    
        return OS_TASK_OK
    }
    
    uint8_t os_task2(void)
    {
        /* do something */
    
        return OS_TASK_OK
    }
    
    uint8_t os_task3(void)
    {
        /* do something */
    
        return OS_TASK_OK
    }
    
    uint8_t os_task4(void)
    {
        /* do something */
    
        return OS_TASK_OK
    }
    
    uint8_t os_task5(void)
    {
        /* do something */
    
        return OS_TASK_OK
    }
    
    uint8_t os_task6(void)
    {
        /* do something */
    
        return OS_TASK_OK
    }
    
    uint8_t os_task7(void)
    {
        /* do something */
    
        return OS_TASK_OK
    }
    
    uint8_t os_task8(void)
    {
        /* do something */
    
        return OS_TASK_OK
    }
    

    三、关于上述时间片轮询结构设计的一些补充

    1、任务调度表的优先级调度设计

    上述设计中,当两个任务同时Ready的时候,仅会按照顺序进行处理。当一些业务功能有优先级功能区分的时候,就无法满足这样的需求,这时候就要对上述设计进行改进。

    (1)任务调度表优先级与优先级计算的设计

  • 任务调度表优先级的设计与就绪任务队列的设计
  • 在原来任务调度表的基础上再新增一个优先级调度表结构体,设计如下:包含优先级(索引)任务调度表任务调度表长度、就绪任务的编号FIFO。

    (注:就绪任务队列中存放着已经准备就绪的任务编号)

    typedef struct
    {
        uint8_t head;
        uint8_t tail;
        uint8_t buf[OS_READY_FIFO_MAX];
    
    }OS_RdyNoFifo_t;
    
    typedef struct 
    {
        uint8_t os_task_number;
        uint8_t (*taskFunc)(void);
        uint8_t os_task_type;
        uint8_t os_task_switch;
        uint8_t os_task_status;
        uint8_t os_task_tickCnt;
        uint8_t os_task_tickInit;
    
    }OS_TASK_SCHEDULE_t;
    
    typedef struct
    {
        uint8_t             prio_number;
        OS_TASK_SCHEDULE_t  *osTaskScheduleCfg;
        uint8_t             *osTaskScheduleCfgSize;
        OS_RdyNoFifo_t      *osRdyNofifoBuf;
    
    }OS_PRIO_SCHEDULE_t;
  • 优先级计算的设计
  • 假如我们需要8层的优先级,我们可以以1个字节中的每一位来充当1级优先级。数值越大,则优先级越高。设计如下:

    /*******************************************************************
     *          OS Priority Schedule Number(Index)
     *******************************************************************/
    #define OS_PRIO_LEVEL_1     0
    #define OS_PRIO_LEVEL_2     1
    #define OS_PRIO_LEVEL_3     2
    #define OS_PRIO_LEVEL_4     3
    #define OS_PRIO_LEVEL_5     4
    #define OS_PRIO_LEVEL_6     5
    #define OS_PRIO_LEVEL_7     6
    #define OS_PRIO_LEVEL_8     7
    
    #define OS_PRIO_LEVEL_MAX   8
    
    #define OS_PRIO_VAL_LEVEL(x)            (1 << (7 - x))
    #define OS_PRIO_VAL_LEVEL_1             (1 << (7 - OS_PRIO_LEVEL_1))
    #define OS_PRIO_VAL_LEVEL_2             (1 << (7 - OS_PRIO_LEVEL_2))
    #define OS_PRIO_VAL_LEVEL_3             (1 << (7 - OS_PRIO_LEVEL_3))
    #define OS_PRIO_VAL_LEVEL_4             (1 << (7 - OS_PRIO_LEVEL_4))
    #define OS_PRIO_VAL_LEVEL_5             (1 << (7 - OS_PRIO_LEVEL_5))
    #define OS_PRIO_VAL_LEVEL_6             (1 << (7 - OS_PRIO_LEVEL_6))
    #define OS_PRIO_VAL_LEVEL_7             (1 << (7 - OS_PRIO_LEVEL_7))
    #define OS_PRIO_VAL_LEVEL_8             (1 << (7 - OS_PRIO_LEVEL_8))

    以2层优先级为例,LEVEL_1与LEVEL_2,则有以下几种情况:

    // 仅LEVEL1 任务产生
    priority = 0x80;
    
    // 仅LEVEL2 任务产生
    priority = 0x40;
    
    // LEVEL1和LEVEL2任务同时产生,此时要先执行LEVEL1的任务
    priority = 0xC0;

     优先级计算函数:

    static bool OS_isScheduleExistTask(OS_RdyNoFifo_t *osRdyNofifoBuf)
    {
        bool ret = false;
        if(osRdyNofifoBuf->head != osRdyNofifoBuf->tail)
        {
            ret = true;
        }
        return ret;
    }
    
    
    static uint8_t OS_SchedulePrioCal(void)
    {
        uint8_t indx = 0;
        uint8_t prio_number = 0;
        uint8_t priority = 0;
        OS_RdyNoFifo_t *osRdyNofifoBuf;
    
        for(indx = 0; indx < osTaskPrioScheduleCfgSize; indx++)
        {
            osRdyNofifoBuf = osTaskPrioScheduleCfg[indx].osRdyNofifoBuf;
            prio_number = osTaskPrioScheduleCfg[indx].prio_number;
    
            if(OS_isScheduleExistTask(osRdyNofifoBuf))
            {
                priority += OS_PRIO_VAL_LEVEL(prio_number);
            }
        }
    
        return priority;
    }
  • 任务调度表与优先级调度表的配置

  • /*********************************************
     *          OS Task Ready FIFO Config 
     *********************************************/
    OS_RdyNoFifo_t OsRdyFifoLv1 = {0};
    OS_RdyNoFifo_t OsRdyFifoLv2 = {0};
    
    /***********************************************************************
     *           OS Task Schedule Config (include priority level)
     ***********************************************************************/
    OS_TASK_SCHEDULE_t osTaskScheduCfg_Lv1[] = 
    {   
        /* number           taskFunc     task_type           task_switch      os_task_status     task_tickCnt    task_tickInit */
        {OS_TASK_NO1,       os_task1,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        0,              100               },
        {OS_TASK_NO2,       os_task2,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        1,              100               },
        {OS_TASK_NO3,       os_task3,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        2,              100               },
    };
    
    OS_TASK_SCHEDULE_t osTaskScheduCfg_Lv2[] = 
    {   
        /* number           taskFunc     task_type           task_switch      os_task_status     task_tickCnt    task_tickInit */
        {OS_TASK_NO1,       os_task4,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        0,              100               },
        {OS_TASK_NO2,       os_task5,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        1,              100               },
        {OS_TASK_NO3,       os_task6,    OS_TASK_CYCLC,      OS_TASK_ON,      OS_TASK_OK,        2,              100               },
    };
    
    uint8_t osTaskScheduleCfgSize_Lv1 = sizeof(osTaskScheduCfg_Lv1) / sizeof(OS_TASK_SCHEDULE_t);
    uint8_t osTaskScheduleCfgSize_Lv2 = sizeof(osTaskScheduCfg_Lv2) / sizeof(OS_TASK_SCHEDULE_t);
    
    /***********************************************************************
     *           OS Task Priority Schedule
     ***********************************************************************/
    OS_PRIO_SCHEDULE_t osTaskPrioScheduleCfg[] = 
    {
        /* prio_number      osTaskScheduleCfg       osTaskScheduleCfgSize            osRdyNofifoBuf */
        {OS_PRIO_LEVEL_1,   &osTaskScheduCfg_Lv1,   &osTaskScheduleCfgSize_Lv1,      &OsRdyFifoLv1},
        {OS_PRIO_LEVEL_2,   &osTaskScheduCfg_Lv2,   &osTaskScheduleCfgSize_Lv2,      &OsRdyFifoLv2},
    };
    
    uint8_t osTaskPrioScheduleCfgSize = sizeof(osTaskPrioScheduleCfg) / sizeof(OS_PRIO_SCHEDULE_t);

    (2)时间片轮询处理函数的修改

    时间片轮询处理函仅做少数改动,遍历整张优先级表的同时,将Ready的任务Push到任务就绪队列中。

    void OS_TmrIsrTask(void)
    {
        uint8_t indx, taskCfgSize, task_i;
        OS_TASK_SCHEDULE_t * osTaskScheduleCfg;
        OS_RdyNoFifo_t * osRdyFifo;
    
        OS_DISABLE_INTERRUPT();
        for(indx = 0; indx < osTaskPrioScheduleCfgSize; indx++)
        {
            osTaskScheduleCfg = osTaskPrioScheduleCfg[indx].osTaskScheduleCfg;
            taskCfgSize = *osTaskPrioScheduleCfg[indx].osTaskScheduleCfgSize;
            osRdyFifo = osTaskPrioScheduleCfg[indx].osRdyNofifoBuf;
    
            for(task_i = 0; task_i < taskCfgSize; task_i++)
            {
                if(osTaskScheduleCfg[task_i].os_task_switch == OS_TASK_ON &&\
                   osTaskScheduleCfg[task_i].os_task_status != OS_TASK_READY)
                {
                    if(osTaskScheduleCfg[task_i].os_task_tickCnt > 0)
                    {
                        osTaskScheduleCfg[task_i].os_task_tickCnt--;
                    }
    
                    if(osTaskScheduleCfg[task_i].os_task_tickCnt == 0)
                    {
                        osTaskScheduleCfg[task_i].os_task_status = OS_TASK_READY;
    
                        if(OS_PushRdyTask(osRdyFifo, osTaskScheduleCfg[task_i].os_task_number) != OS_FIFO_OK)
                        {
                            /* push error dispose */
                        }
    
                        switch(osTaskScheduleCfg[task_i].os_task_type)
                        {
                            case OS_TASK_CYCLC:
                            {
                                osTaskScheduleCfg[task_i].os_task_tickCnt = osTaskScheduleCfg[task_i].os_task_tickInit;
                                break;
                            }
                            case OS_TASK_EVENT:
                            {
                                break;
                            }
                        }
    
    
                    }
                        
                }
    
            }
    
        }
        OS_ENABLE_INTERRUPT();
    
    }
    

    (3)任务调度函数的修改

    任务调度函数则需要先计算优先级,看优先处理哪一张任务调度表,再去从对应优先级的任务就绪队列中Pop出任务编号,然后执行对应任务。

    static void OS_StartPrioSchedule(uint8_t prio_number)
    {
        uint8_t prio_i, task_i;
        uint8_t task_number, osTaskScheduleCfgSize;
        OS_TASK_SCHEDULE_t * osTaskScheduleCfg;
        OS_RdyNoFifo_t * osRdyFifo;
    
        OS_DISABLE_INTERRUPT();
    
        osTaskScheduleCfg = osTaskPrioScheduleCfg[prio_number].osTaskScheduleCfg;
        osTaskScheduleCfgSize = *osTaskPrioScheduleCfg[prio_number].osTaskScheduleCfgSize;
        osRdyFifo = osTaskPrioScheduleCfg[prio_number].osRdyNofifoBuf;
    
        if(OS_PopRdyTask(osRdyFifo, &task_number) == OS_FIFO_OK)
        {
            switch(osTaskScheduleCfg[task_number].os_task_status)
            {
                case OS_TASK_OK:
                {
                    break;
                }
                case OS_TASK_ERROR:
                {
                    break;
                }
                case OS_TASK_READY:
                {
                    osTaskScheduleCfg[task_number].os_task_status = osTaskScheduleCfg[task_number].taskFunc();
                    if(osTaskScheduleCfg[task_number].os_task_type == OS_TASK_EVENT)
                    {
                        osTaskScheduleCfg[task_number].os_task_switch = OS_TASK_OFF;
                    }
                    break;
                }
            }
        }
        OS_ENABLE_INTERRUPT();
    }
    
    
    
    void OS_StartSchedule(void)
    {
        uint8_t indx;
        uint8_t priority;
        uint8_t task_number;
    
        for(;;)
        {
            /* Calculate the Schedule Priority */
            priority = OS_SchedulePrioCal();
            if(priority >= OS_PRIO_VAL_LEVEL_1)
            {
                OS_StartPrioSchedule(OS_PRIO_LEVEL_1);
            }
            else if(priority >= OS_PRIO_VAL_LEVEL_2)
            {
                OS_StartPrioSchedule(OS_PRIO_LEVEL_2);
            }
        }   
    }
    物联沃分享整理
    物联沃-IOTWORD物联网 » 单片机简易时间片轮询结构设计的工作经验总结

    发表回复