• 合作式调度器Cooperative Scheduler


    背景

    单片机开发,入门从最开始的IO置位(点亮LED)开始,裸机开发来说整个是面向过程开发,最终所有的功能都在一个While循环之中,这样的好处在于模块逻辑很直观,流程比较清晰,但是在程序功能增多的时候整体功能会显得不直观。
    且需要很多的标志位,同时由于所有的功能循环在一起运行,对于任务的运行间隔并不能很好的确定,有时也会因一个任务时间过长导致别的任务没有及时得到响应。
    现在市面上的嵌入式RTOS也很多,这些RTOS可以很好设置任务间隔,这样用户可以根据自己应用来定义。
    不过RTOS一般都需要一些汇编的知识,入门门槛较高,且RTOS成功用于应用需要对RTOS有一个很好的了解,且应用编写也需要一定的规则与要求,对于初学者来说难度较大。
    合作式调度器是一种简单且可预测的调度方式,能较好地满足,任务按时调度的需求。同时合作式调度器全部有C语言实现,便于理解与移植。可以应用于很多的电子类产品,简单的复杂的,消费品及工业品

    合作式调度器

    调度器

    可以看作是一个简单的操作系统,允许以周期性或单次方式来调用任务。从底层来看,为不同任务提供定时器中断服务,即初始化一个定时器为不同任务报价定时调用的功能。
    嵌入式的RTOS也是一种调度器,一般都是可抢占的调度器,类似于freertos。抢占式调度器,通常较为复杂,需要实现任务的上下文切换,添加各种安全机制,锁机制,临界段,消息机制等。
    合作式调度器,只是想实现定时调度任务,可以不需要额外的机制,实现简单。同时对过程来说,是很可控的。不会因为一些作务的切换导致运行不正常。

    实现步骤

    1. 数据结构
    2. 初始化任务空间
    3. 定时器中断服务程序,用于刷新调度器
    4. 向调度器增加任务的函数
    5. 调度函数,可在运行循环函数内运行,即调度器更新去运行准备好的任务
    6. 删除任务函数,可以不添加,因为有些系统可以不删除任务

    代码示例

    任务数据结构

    typedef struct sTask
    {
    	void (*pTask)(void);
    	uint32 delay;
    	uint32 period;
    	uint8 runMe;
    }sTask;
    

    总的任务空间

    #define SCH_MAX_TASKS    10
    sTask schTaskGroup[SCH_MAX_TASKS];
    

    初始化任务空间

    void Task_Framework_Init(void)
    {
    	uint8 i = 0;
    	
    	for(i = 0; i < SCH_MAX_TASKS; i++)
    	{
    		schTaskGroup[i].pTask = 0;
    		schTaskGroup[i].delay = 0;
    		schTaskGroup[i].period = 0;
    		schTaskGroup[i].runMe = 0;
    	}
    }
    

    定时器中断更新调度器

    void Task_Framework_Update(void)
    {
    	uint8 index = 0;
    	
    	for(index = 0; index < SCH_MAX_TASKS; index++)
    	{
    		if(schTaskGroup[index].pTask)
    		{
    			if(schTaskGroup[index].delay == 0)
    			{
    				schTaskGroup[index].runMe += 1;
    				
    				if(schTaskGroup[index].period)
    				{
    					schTaskGroup[index].delay = schTaskGroup[index].period - 1;
    				}
    			}
    			else
    			{
    				schTaskGroup[index].delay -= 1;
    			}
    		}
    	}
    }
    

    添加任务

    uint8 Task_Framework_Add_Task(void (*pTask_)(void), uint32 delay_, uint32 period_)
    {
    	uint8 index = 0;
    	
    	while((schTaskGroup[index].pTask != 0) && index < SCH_MAX_TASKS)
    	{
    		index++;
    	}
    	
    	if(index == SCH_MAX_TASKS)
    	{
    		return SCH_MAX_TASKS;
    	}
    	
    	schTaskGroup[index].pTask = pTask_;
    	schTaskGroup[index].delay = delay_;
    	schTaskGroup[index].period = period_;
    	schTaskGroup[index].runMe = 0;
    	
    	return index;
    }
    

    调度函数

    调度函数可以在定时器中断中,在更新完直接调用,也可以在主循环中调用。个人倾向于在主循环中调用,减少在中断操作。

    void Task_Framework_Dispatch_Tasks(void)
    {
    	uint8 index = 0;
    	
    	for(index = 0; index < SCH_MAX_TASKS; index++)
    	{
    		if(schTaskGroup[index].runMe > 0)
    		{
    			(*(schTaskGroup[index].pTask))();
    			schTaskGroup[index].runMe -= 1;
    			
    			if(schTaskGroup[index].period == 0)
    			{
    				Task_Framework_Delete_Task(index);
    			}
    		}
    	}
    }
    

    删除任务

    没有删除任务需求,这个函数也可以不添加。

    uint8 Task_Framework_Delete_Task(uint8 taskId_)
    {
    	uint8 errorCode = 0;
    	if(taskId_ < SCH_MAX_TASKS)
    	{
    		schTaskGroup[taskId_].pTask = 0;
    		schTaskGroup[taskId_].delay = 0;
    		schTaskGroup[taskId_].period = 0;
    		schTaskGroup[taskId_].runMe = 0;
    	}
    	
    	return errorCode;
    }
    

    demo

    int main( void )
    {
    	System_Framework_Init();
    	Task_Framework_Add_Task(App_Test, 0, 100);
            Task_Framework_Add_Task(App_Polling, 0, 200);
    	
    	while(1)
    	{
    		Task_Framework_Dispatch_Tasks();
    	}
    }
    
    void timer_interrupt(void)
    {
            Task_Framework_Update();
    }
    

    总结

    通过上述的代码就可以实现一个合作式调度器,对于不同的MCU可以很快的移植过去使用。
    当然合作式调度器使用中也有一点地方要注意的。

    1. 每个任务的运行时间不可以过长,否则会影响系统的整体时效性。因此一些长时间运行的任务需要进行分解,使每次任务调用,不用过长的时间
    2. 所有的运行任务采用一个相同的delay,可能会导致任务的运行时间会有轻微偏差,如果对这一点要求过高,可以不同任务采用不同的delay,使调度任务时,尽量只有一个任务是需要调度的

    实际的现实效果,可以在系统完成,添加一些测量函数,来评估下当前调度器的运行状态。
    总体来说,合作式用于简单的电子应用,是一个很好的实现机制。
    示例代码

    参考书籍:时间触发嵌入式系统设计模式

  • 相关阅读:
    偶的机机升级了
    质疑 Sina.com 的金牌榜[图文]
    一道JAVA作业题
    北京出差总结
    我拿什么奉献给你
    CSDN无限极树PHP+MySQL版
    极大强连通分量的Tarjan算法
    NOI2001 炮兵阵地详解
    单调队列及其应用
    some english website
  • 原文地址:https://www.cnblogs.com/stupidpeng/p/15772305.html
Copyright © 2020-2023  润新知