微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 基于单片机的协程多任务

基于单片机的协程多任务

时间:11-25 来源:互联网 点击:

在很多的单片机项目中,由于操作系统的体积以及使用的背景知识,如果采用的话,可能让项目脱离主要业务方向,这个时候很有必要使用简单的协程多任务

1协程多任务的特点

每个任务优先级平等

每个任务主动释放CPU控制权

2 UCOS等操作系统的特点

任务存在不同优先级,很方便进行CPU资源的分配。

对于ucos的任务来说,每个任务都认为自己是独占cpu的,可以随便休眠之类,这样对于代码的风格的限制比较小,比较好修改现存的模块。

操作系统一般提供比较多的服务,对于复杂应用比较好。

3 协程多任务的应用场合

协程方式适合简单的多任务,每个任务要确认其符合协程的模型,不能阻塞cpu运行,要主动释放CPU,如果考虑到多人合作,或者引入第三方的代码,进来,那么协程方式恐怕工作量过大(模型改造检查),这时最好使用操作系统。

协程或者操作系统的平台的建立,这些都需要积累,也没有什么高级和低级的区别,只是针对不同的场景和开发人员选择不同而已。

4协程多任务的实现

为了使用方便,并且将来便于系统升级,多任务采用基于接口的方式定义和实现。

4.1 任务的定义

每个任务会管理自己的数据,提供对外接口,每个任务提供以下形式结构

Struct _task1

{

//任务对外接口,函数指针

Void (*start)(Struct _task1*handle);

Void (*run)(Struct _task1*handle);

Void (*stop)(Struct _task1*handle);

//其他对外接口

...

//任务私有数据

} task1;

之所以将对外接口放在任务结构体中,是为了强调这些是任务的对外接口,而且客户只能调用这里面的接口,另外函数指针也很方便的提供了一个接口和实现的分隔层

4.2 任务的实现

任务的实现层面,用户可以根据对系统和业务的理解,做系统演化,而不会影响到外部接口的使用

//任务接口实现函数

Void task1_start(Struct _task1*handle)

{

//设置任务开始标识

}

Void task1_run(Struct _task1*handle)

{

If(handle->status1)

{

//处理

}

Else if(handle->status2)

{

//处理

}

}

Void task1_stop(Struct _task1*handle)

{

//设置任务结束标识

}

//任务初始化函数,构造任务结构体

Void task1_init(Struct _task1*handle)

{

Handle->start = task_start;

Handle->run = task_run;

Handle->stop= task_stop;

}

4.3 调用形式

4.3.1 定义全局任务结构体

Struct _task1

{

//任务对外接口,函数指针

Void (*start)(Struct _task1*handle);

Void (*run)(Struct _task1*handle);

Void (*stop)(Struct _task1*handle);

//其他对外接口

...

//任务私有数据

} task1;

4.3.2 任务初始化

Task1_init(&task1);

4.3.3 开始任务

Task1->start(&task1);

4.3.4 结束任务

Task1->stop(&task1);

4.3.5 任务运行

一般系统在启动后运行的一个无限循环语句中有

While(1)

{

Task1->run(&task1);

Task2->run(&task2)

...其他任务运行

}

5 定时器以及延时的实现方法

协程多任务不会在代码中使用sleep()阻塞CPU的方法做定时器或者休眠,定时器会有一个单独的任务,任务提供新增定时器、删除定时器、查询定时器值等接口来提供软件定时器功能。

6 任务间通讯

原则上通过任务提供的接口来通讯,当然如果通讯工作量过大,不反对使用第三方任务来完成通讯。

7 驱动层模块任务例子

对于单片机协程多任务来说,应用层和驱动层是优先级相同的任务,对于很多的驱动来说,例如UART驱动,其发送和接收很多采用的是查询的方式来进行,下面简单的使用多任务的结构方式来说明驱动的写作

Struct _com{

//任务对外接口,函数指针

Void (*start)(Struct _com*handle);

Void (*run)(Struct _com*handle);

Void (*stop)(Struct _com*handle);

//其他对外接口

Void read(char* ch)

Void write(char* ch)

...

//任务私有数据

//驱动状态,

Char status;

}com1;

//运行代码

Void Com_run(Struct _com * handle)

{

If(read)

{

If(register1)

{

}

Else if(register2)

{

}

}

Else if(write)

{

If(register3)

{

}

Else if(register4)

{

}

}

}

从上面代码可见,驱动模块其实和普通的任务没有区别。

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top