合作式调度器和混合式调度器

2019-08-17 00:27发布

额, 一般情况下, 如果项目不是很复杂, 我们倾向于采用裸机开发, 如果项目稍微复杂点, 我们就倾向于采用RTOS, 例如UCOS, FreeRTOS...

其实, 我们还有一条更加"中庸"的道路...


注意事项如下:

1> 如下代码参考于<<时间触发嵌入式系统设计模式>>...


2> 我的demo实验环境: MDK5 + 战舰F1开发板  



3> 代码的注释部分请直接代码(我在代码关键部分都写中文注释了)


4> 此部分代码我只是在实验中验证过, 用在项目上请自行修改和评估(书上代码被我修改了一点).


5> 欢迎大家批评指正, 期望能看到大神修改,优化,并最终产生一个可供大家用在实际项目的源码...


6> 最后附上程序demo (<<时间触发嵌入式系统设计模式>>pdf太大, 大家自行到网上下载...)


/***********************************************************************************************
* 移植注意事项:
* <1> 调度器最多支持任务数目不能超过SCHEDULER_MAX_TASK_NUM.
* <2> SCHEDULER_DEBUG=1开启调试, =0关闭调试.
* <3> 每个任务的运行时间应当小于时标间隔, 可以通过SCHEDULER_DispatchTask()中的SCHEDULER_Assert()进行测试.
* <4> 尽量不要让任务运行时间重叠, 可使用Delay进行调节.
* <5> 如果有必要的话, 可以让MCU在运行完任务之后, 未进入下一个时标的时间间隔内进入休眠模式(通过定时中断唤醒), 降低功耗.
* <6> 所有的任务都必须是非阻塞的, 包括任务中所调用的函数.
*
* 混合式调度器临界问题:
* <1> 抢占式任务 因为它们不能被中断, 所以不存在在检查和加锁之间出现中断.
* <2> 合作式任务 可能会在检查和加锁之间被中断, 但是抢占式任务会一直执行到完成, 所以系统的状态会恢复到抢占式任务抢占之前的状态.
* if (xxx == LOCKED) return;
* xxx = LOCKED;
* 处理数据...
* xxx = UNLOCKED;
*
* 混合式调度器注意事项:
* <1> 必须保证任务不会重叠.
* <2> 实现一个抢占式任务, 该任务一般(然而并非一定)将在每个时标被调用, 该抢占式任务一般用来处理紧急事件(快速响应外部事件).
* <3> 抢占式任务必须尽量简短, 不要超过时标间隔一半!!!
***********************************************************************************************/


主要数据结构:
[mw_shl_code=c,true]typedef struct
{
        volatile uint32_t Delay;                                              /* 任务具体的延时时间(以定时器的时标为单位) */
        
        volatile uint32_t Period;                                             /* 任务两次运行的时间间隔(如果是周期运行的任务)                           */
        
        volatile uint32_t RunCounts;                                         /* 任务可运行的次数(如果是0, 表示当前任务没有运行的权限) */

        volatile uint32_t Cooperation;                  /* =1表示是合作式调度任务, =0表示是抢占式调度任务 */      

        TaskCallBack_t Task;                            /* 任务回调函数         */

        void *Arg;                                      /* 任务回调函数参数  */
}Scheduler_t;[/mw_shl_code]

1> 任务队列
[mw_shl_code=c,true]/* 任务队列池(所有的任务都是放在这个队列池中) */
static Scheduler_t SchedulerTasksPool[SCHEDULER_MAX_TASK_NUM]; [/mw_shl_code]

2> 初始化调度器
[mw_shl_code=c,true]void SCHEDULER_Init(void)
{
        uint32_t index;

        /* 初始化硬件定时器, 以产生标准的定时时标 */
        TIM2_Init();

        /* 调用SCHEDULER_DeleteTask初始化任务相关成员 */
        for (index=0; index<SCHEDULER_MAX_TASK_NUM; index++)
        {
                SCHEDULER_DeleteTask(index);
        }
}[/mw_shl_code]

3> 添加任务
[mw_shl_code=c,true]uint32_t SCHEDULER_AddTask(TaskCallBack_t const Task, void * const Arg, uint32_t Delay, uint32_t Period, uint32_t Cooperation)
{
        uint32_t index = 0;

        while ((SchedulerTasksPool[index].Task) && (index<SCHEDULER_MAX_TASK_NUM)) index++;
        
        #if SCHEDULER_DEBUG
        SCHEDULER_Assert(index>=SCHEDULER_MAX_TASK_NUM, "The task index is out of range!!!");
        #endif

        SchedulerTasksPool[index].Delay = Delay;
        SchedulerTasksPool[index].Period = Period;
        SchedulerTasksPool[index].RunCounts = 0;
        SchedulerTasksPool[index].Cooperation = Cooperation;
        SchedulerTasksPool[index].Task = Task;
        SchedulerTasksPool[index].Arg = Arg;

        return index;
}[/mw_shl_code]

4> 删除任务
[mw_shl_code=c,true]void SCHEDULER_DeleteTask(uint32_t TaskIndex)
{
        if (TaskIndex >= SCHEDULER_MAX_TASK_NUM) return;

        if (SchedulerTasksPool[TaskIndex].Task == NULL) return;

        SchedulerTasksPool[TaskIndex].Delay = 0;
        SchedulerTasksPool[TaskIndex].Period = 0;
        SchedulerTasksPool[TaskIndex].RunCounts = 0;
        //SchedulerTasksPool[TaskIndex].Cooperation = 1;
        SchedulerTasksPool[TaskIndex].Task = NULL;
        SchedulerTasksPool[TaskIndex].Arg = NULL;
}[/mw_shl_code]

5>启动任务
[mw_shl_code=c,true]void SCHEDULER_StartTask(void)
{
        TIM_Cmd(TIM2, ENABLE);
}[/mw_shl_code]

6> 调度和运行任务
[mw_shl_code=c,true]void SCHEDULER_DispatchTask(void)
{
        uint32_t index;

        for (index=0; index<SCHEDULER_MAX_TASK_NUM; index++)
        {
                /* 如果当前任务Task未被注册呢?
                 * 由于一开始已经初始化了调度器, 如果未初始化Task, 那此时Count肯定=0 */
                if ((SchedulerTasksPool[index].RunCounts > 0) && (SchedulerTasksPool[index].Cooperation))
                {
                        SchedulerTasksPool[index].RunCounts--;
                        SchedulerTasksPool[index].Task(SchedulerTasksPool[index].Arg);

                        if (SchedulerTasksPool[index].Period == 0)
                        {
                                SCHEDULER_DeleteTask(index);
                        }

                        /* 为了保证所有任务的运行时间<时标间隔, 最好的测试方法是将每个任务的Period设置为1, 并开启SCHEDULER_DEBUG,
                        测试RunCounts是否会一直增加, 如果会的话, 说明当前任务的运行时间超过一个时标间隔. */
                        #if SCHEDULER_DEBUG
            SCHEDULER_Assert(SchedulerTasksPool[index].RunCounts>1, "The task execution time is out of range!!!");
            #endif
                }
        }

        //如果满足任务运行条件, 如果任务运行时间小于时标间隔, 那么剩余时间可以让MCU进行休眠, 降低功耗...
        //如果不满足任务运行条件, 那么整个时标间隔(的所有时间)都让MCU进行休眠, 降低功耗...
}
[/mw_shl_code]

7>调度更新
[mw_shl_code=c,true]void SCHEDULER_Update(void)
{
        uint32_t index;

        for (index=0; index<SCHEDULER_MAX_TASK_NUM; index++)
        {
                if (SchedulerTasksPool[index].Task)
                {
                        if (SchedulerTasksPool[index].Delay == 0)
                        {
                                /* 合作式调度任务 */
                                if (SchedulerTasksPool[index].Cooperation)
                                {
                                        SchedulerTasksPool[index].RunCounts++;
                                }
                                /* 抢占式调度任务 */
                                else
                                {
                                        SchedulerTasksPool[index].Task(SchedulerTasksPool[index].Arg);
                                        /* 如果是抢占式调度任务, 保留RunCounts似乎没有意义 */
                                        //SchedulerTasksPool[index].RunCounts--;

                                        if (SchedulerTasksPool[index].Period == 0)
                                        {
                                                SchedulerTasksPool[index].Task = NULL;
                                        }
                                }
                                
                                if (SchedulerTasksPool[index].Period > 0)
                                {
                                        SchedulerTasksPool[index].Delay = SchedulerTasksPool[index].Period;
                                }
                        }
                        else
                        {
                                SchedulerTasksPool[index].Delay--;
                        }
                }
        }
}
[/mw_shl_code]


0条回答

一周热门 更多>