• 自用纯C语言实现任务调度(可用于STM32、C51等单片机)


    前言

      这个任务调度模块的实现是形成于毕设项目中的,用在STM32中,断断续续跨度2个月实现了一些基本功能,可能后面再做其他项目时会一点点完善起来,也会多学习相关知识来强化模块的实用性和高效性,毕竟用自己自主实现出来的功能还是蛮舒心的。

    任务调度模式结构

      整体上的结构属于线性结构,结合链表定时器来实现,我使用的是sysTick这个滴答时钟,1ms的频率,功能比较简单,容易理解。

    分片

      分片的模式,主要体现在函数分片时间分片在我之前就有使用在函数中,主要的思路是,把函数功能切片,分为几个小部分,每次执行时按次序执行小部分,对于没有时序要求的函数来说,可以把一个占用CPU大的功能分摊开来实现,从而避免有些地方耗时长的问题。对于时间分片,其实就是定时器的一种应用,实际上,函数分片在执行的时候已经是一种时间分片了,不过现在加上人为的控制在里面了。
      下面是函数分片的一般结构:

    void func(char *fos,...){
        static char step=0;//顺序控制变量,自由度比较高,可乱序,可循环,可延迟执行
        switch(step){
            case 0:{
                //...
                step++;
                break;
            }
            case 1:{
                //...
                step++;
                break;
            }
            //...
            default:{
                //step++;//可以借助default实现延时的效果,即跳过几次空白step
                break;
            }
    
        }
        return;
    }
    

    其中添加的参数变量*fos必要的,因为就是通过传入每个任务的这个标志位来判断是否运行结束,而其他的参数,就得基于具体任务做不一样的处理了。

    轮询

    • 运行框图

      可以看到这个框图是一个头尾相连闭环结构,从头节点依次运行到尾节点后再从头循环往复执行下去。

    • 轮询函数
    void loop_task(void){
    	static Task_Obj *tasknode;
    	
    	tasknode=task_curnode->next;//repoint the curnode to the next
    	if(tasknode==NULL){//tasknode is null,only the headnode have the attr
    		return;//express the task space is none
    	}
    	else if(tasknode->task_type==TYPE_HEAD){//tasknode is headnode
    		task_curnode=tasknode;
    		return;
    	}
    	else{
    		if(tasknode->run_type == RUN_WAIT){
                //等待型任务,通过ready标志来确定是否执行,否则就跳过
    			if(!tasknode->ready){
    				if(task_curnode->next !=NULL){
    					task_curnode=task_curnode->next;
    					return;
    				}
    			}
    		}
    		if(tasknode->task_status==STATUS_INIT){
    
    			tasknode->tickstart=HAL_GetTick();//获取tick
    			tasknode->task_status=STATUS_RUN;
    
    		}
    		else if(tasknode->task_status==STATUS_RUN){
    			if((HAL_GetTick() - tasknode->tickstart) > (uint32_t)tasknode->task_tick){
    				tasknode->task_name(&(tasknode->task_fos));//run the step task,transfer the fos
    				tasknode->tickstart+=(uint32_t)tasknode->task_tick;//update the tickstart
    			}
    		}
    		
    	}
    	if(tasknode->task_fos==FOS_FLAG){
    		
    		tasknode->ready=0;
    		if(tasknode->waittask!=NULL){
                //置位该任务绑定的等待的任务准备运行标志位,标识可以准备运行了
    			tasknode->waittask->ready=1;
    		}
            //运行结束就删掉该任务
    		delete_task(tasknode);
    	}
    	else if(tasknode->task_fos==FOC_FLAG){
            //循环运行该任务
    		tasknode->task_status=STATUS_INIT;//continue running from start
    		tasknode->task_fos=0;//RESET fos
    		
    	}
    	if(task_curnode->next !=NULL){
    		if(task_curnode->next->run_type==RUN_FORCE) return;//force-type's task
    		
    		else task_curnode=task_curnode->next;
    		
    	}
    	
    
    }
    

    其中有几个运行态和标志位

    #define FOS_FLAG 99//运行结束标志
    #define FOC_FLAG 100//运行结束后再次执行,相当于循环运行
    #define TYPE_NOMAL 0//标识一般任务类型
    #define TYPE_HEAD 1//标识头任务类型
    #define TYPE_END 2//标识尾任务类型
    #define RUN_NORMAL 0//一般轮询模式
    #define RUN_FORCE 1//强制运行该任务,运行结束才继续下一个任务
    #define RUN_WAIT 2//等待指定的任务结束,才可以被运行
    #define STATUS_INIT 0//任务的准备阶段,用于获取起始时间
    #define STATUS_RUN 1//任务运行阶段
    #define STATUS_UNVAILED 2//无效状态
    

    运行时对时间间隔tick的把握还有点问题,这个等待后面有机会优化下。

    调度实现

    • 任务链表结构
    typedef struct TASK_CLASS{
    	void (*task_name)(char *taskfos,...);//任务函数
    	int task_tick;//任务的时间分片间隔
    	uint32_t tickstart;//起始时间点,每次执行完须加上一个tick
    	char task_fos;//运行结束标志
    	char task_type;//任务类型变量
    	char task_status;//任务状态
    	char run_type;//运行状态
    	char ready;//准备运行标志位
    	struct TASK_CLASS *next;//下一任务
    	struct TASK_CLASS *waittask;//等待执行的任务
    } Task_Obj;
    
    • 添加任务

      • add_task
      void add_task(void (*taskname)(char *,...),int tasktick,int runtype){//可变参,这里未做处理
      Task_Obj *tasknode,*tmpnode;
      char i;
      
      tasknode = (Task_Obj*)malloc(sizeof(Task_Obj));
      
      tasknode->task_name=taskname;
      tasknode->task_tick=tasktick;
      tasknode->task_fos=0;
      tasknode->task_status=STATUS_INIT;//initial status
      tasknode->task_type=TYPE_END; //set the new node to endnode
      tasknode->run_type=runtype;
      tasknode->next=&task_headnode;//the endnode point to the headnode
      
      tmpnode=&task_headnode;
      if(task_num==0){
      	tmpnode->next=tasknode;
      	task_num++;
      	return;
      }
      for(i=0;inext;//reach the endnode
      }
      tmpnode->task_type=TYPE_NOMAL;//turn the last endnode to the normal node
      tmpnode->next=tasknode;
      task_num++;
      }
      
      • add_wait_task
      void add_wait_task(void (*taskname)(char *),void (*waitname)(char *),int tasktick){
      Task_Obj *tmpnode,*tasknode;
      char i,pos;
      
      tmpnode=&task_headnode;
      for(i=0;inext;//reach the endnode
      	if(tmpnode->task_name==taskname){
      		pos=i;//获取要等待任务的位置
      		break;
      	}
      }
      
      tasknode = (Task_Obj*)malloc(sizeof(Task_Obj));
      
      tasknode->task_name=waitname;
      tasknode->task_tick=tasktick;
      tasknode->task_fos=0;
      tasknode->task_status=STATUS_INIT;//initial status
      tasknode->task_type=TYPE_END; //set the new node to endnode
      tasknode->run_type=RUN_WAIT;//任务为等待运行
      tasknode->ready=0;
      tasknode->next=&task_headnode;//the endnode point to the headnode
      
      tmpnode->waittask=tasknode;//获取新建的等待执行的任务地址,在运行结束后把等待执行的任务的准备运行标志位置1
      
      tmpnode=&task_headnode;
      if(task_num==0){
      	tmpnode->next=tasknode;
      	task_num++;
      	return;
      }
      for(i=0;inext;//reach the endnode
      }
      tmpnode->task_type=TYPE_NOMAL;//turn the last endnode to the normal node
      tmpnode->next=tasknode;
      task_num++;
      
      }
      
    • 删除任务

      • delete_task(局限性大,只针对当前运行的任务而言)
      void delete_task(Task_Obj *taskobj){
      if(task_curnode->task_type==TYPE_HEAD && task_num < 2){//if curnode is headnode,and tasknum=1
      	task_curnode->next=NULL;
      }
      else{
      	task_curnode->next=taskobj->next;//repoint the curnode next
      }
      free(taskobj);//free the space of where the taskobj pointed
      
      task_num--;
      
      }
      
      • delete_task_withname(删除指定任务名的任务)
      void delete_task_withname(void (*taskname)(char *)){
      Task_Obj *tmpnode,*tmpnode2;
      char i,pos;
      
      tmpnode=&task_headnode;
      for(i=0;inext;//reach the endnode
      	if(tmpnode->task_name==taskname){
      		pos=i;
      		break;
      	}
      }
      if(i==task_num) return;
      tmpnode=&task_headnode;
      for(i=0;i1;i++){
      	tmpnode2=tmpnode;
      	tmpnode=tmpnode->next;
      }
      if(tmpnode->next==NULL){//if tmpnode is endnode
      	tmpnode2->next=&task_headnode;
      }
      else{
      	tmpnode2->next=tmpnode->next;//repoint the curnode next
      }
      task_num--;
      free(tmpnode);
      }
      
    • 初始化任务空间

    void non_task(char *taskfos){
    	return;
    }
    
    void init_taskspace(void){
    	task_headnode.task_name=non_task;
    	task_headnode.task_type=TYPE_HEAD;
    	task_headnode.task_status=STATUS_UNVAILED;
    	task_headnode.next=NULL;
    	task_curnode=&task_headnode;//头节点是没有任务需要执行的
    	task_num=0;
    }
    
    • 调用实例
    add_task(task1,500,RUN_NORMAL);//500ms执行一次task1任务
    add_wait_task(task1,task2,500);//task2等待task1结束才会执行,运行的时间间隔为500ms
    delete_task_withname(task1);//删除task1任务
    
    while(1){
        //...
        loop_task();//任务轮询
    }
    

    结语

      整体实现说难不难,说简单不简单,但也是我第一次尝试这种偏向系统级应用的代码,而且都没有参照任何其他的资料和代码,完全以自己的对任务的理解和具体项目的需求来一点点实现,希望后面会把这个调度的代码进一步完善成一个通用型的调度方式,也方便后面项目的使用了。

  • 相关阅读:
    基于安卓(Android)的即时实时聊天APP软件
    92.(cesium之家)cesium楼栋分层
    HDLBits: 在线学习 SystemVerilog(十二)-Problem 65-71(加法器)
    G1D23-RAGA&报名蓝桥&Attackg&安装cuda&torch
    Java中如何设置注释模板呢?
    MySQL-慢查询日志
    【Python】学生管理系统——详细解释+代码+详细注释(课设必过)
    回归预测 | MATLAB实现IWOA-LSTM改进鲸鱼算法算法优化长短期记忆神经网络的数据回归预测(多指标,多图)
    人工智能基础第一次作业
    新生儿长牙期:原因、科普和注意事项
  • 原文地址:https://www.cnblogs.com/pie-o/p/17300699.html