前言

这个任务调度模块的实现是形成于毕设项目中的,用在
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;i<task_num;i++){
    	tmpnode=tmpnode->next;//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;i<task_num;i++){
    	tmpnode=tmpnode->next;//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;i<task_num;i++){
    	tmpnode=tmpnode->next;//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;i<task_num;i++){
    	tmpnode=tmpnode->next;//reach the endnode
    	if(tmpnode->task_name==taskname){
    		pos=i;
    		break;
    	}
    }
    if(i==task_num) return;
    tmpnode=&task_headnode;
    for(i=0;i<pos+1;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();//任务轮询
}

结语

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

标签: none

添加新评论