利用C语言实现任务调度的示例代码

 更新时间:2023年04月10日 08:45:17   作者:pie_thn  
这篇文章主要为大家详细介绍了如何利用纯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;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();//任务轮询
}

结语

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

到此这篇关于利用C语言实现任务调度的示例代码的文章就介绍到这了,更多相关C语言任务调度内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 深入理解C语言 static、extern与指针函数

    深入理解C语言 static、extern与指针函数

    这篇文章主要介绍了C语言 static、extern与指针函数,有需要的朋友可以参考一下
    2013-12-12
  • C/C++开发中extern的一些使用注意事项

    C/C++开发中extern的一些使用注意事项

    这篇文章主要为大家介绍了C/C++开发中extern一些使用注意事项的事例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • C++实现通讯录小功能

    C++实现通讯录小功能

    这篇文章主要为大家详细介绍了C++实现通讯录小功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • 深入线性时间复杂度求数组中第K大数的方法详解

    深入线性时间复杂度求数组中第K大数的方法详解

    本篇文章是对线性时间复杂度求数组中第K大数的方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C++不使用变量求字符串长度strlen函数的实现方法

    C++不使用变量求字符串长度strlen函数的实现方法

    这篇文章主要介绍了C++不使用变量求字符串长度strlen函数的实现方法,实例分析了strlen函数的实现原理与不使用变量求字符串长度的实现技巧,需要的朋友可以参考下
    2015-06-06
  • C++实现各种排序算法类汇总

    C++实现各种排序算法类汇总

    这篇文章主要介绍了C++实现各种排序算法类,需要的朋友可以参考下
    2014-07-07
  • C语言从代码中加载动态链接库过程解析

    C语言从代码中加载动态链接库过程解析

    这篇文章主要介绍了C语言从代码中加载动态链接库过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • 详解Matlab如何绘制小提琴图

    详解Matlab如何绘制小提琴图

    小提琴图 (Violin Plot)是用来展示多组数据的分布状态以及概率密度。这种图表结合了箱形图和密度图的特征,主要用来显示数据的分布形状。本文将介绍如何利用Matlab绘制小提琴图,需要的可以参考一下
    2022-02-02
  • C语言开发简易版扫雷小游戏

    C语言开发简易版扫雷小游戏

    本文给大家分享的是一个使用C语言开发的命令行下的简易版扫雷小游戏,本身没有什么太多的技术含量,只不过是笔者的处女作,所以还是推荐给大家,希望对大家学习C能够有所帮助。
    2015-12-12
  • C++11, 14, 17对tuple元素的访问详情

    C++11, 14, 17对tuple元素的访问详情

    这篇文章主要介绍了C++11, 14, 17对tuple元素的访问详情,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11

最新评论