裸机编程时,由于没有类似于操作系统的任务调度这种可以管理任务的功能,所以各任务的执行都是一个接着一个顺序执行,这样很容易因为其中的一个任务占据了单片机过多的时间而造成别的任务响应不及时(比如按键事件的到来),那么如何实现并行运行,使各任务及时在有效的时间内得到单片机CPU的使用权呢?
这里所提到的并行多任务程序开发就是解决这种问题的一种有效方法。当然,这里的并行并不是真正的并行,因为单片机是一种单核的CPU,一次只能执行一条程序,这里的并行指的则是一种类似并行的效果。
这样也道出了我们并行多任务程序开发的核心思想:对任务进行有效的管理,任务依据要求区别对待,依据要求分配CPU使用时间。也就是说,以while(1)主循环为一个系统周期,因为要让各个任务及时响应,我们的这个系统周期应该尽量短,但是有些任务耗时又特别长,这就形成了矛盾。所以让需要及时响应的任务占据系统周期的时间份额越大,而那些耗CPU时间比较大的程序,我们将其拆分成几个部分,一个系统周期只运行其中的一部分,下一部分就等待下一个系统周期的到来。这样的话,我们使各个任务都能在有限的时间内得到响应,加之人类对时间的感知力的迟钝,从而使任务就像并行运行一样。
下面举个栗子。(例子代码来源于书籍:《单片机编程魔法师之高级裸编程思想》)
我们有三个任务,我们让其在每个系统周期(主函数的while循环)内执行任务其中的一部分,这样一个系统周期的时间就特别短,每个任务都能得到及时的响应。
注:程序中有一些是对任务生命状态进行管理的标志位,增加了系统管理任务的能力。
程序很简单,注释很详细,在此就不赘述了。
#include <stdlib.h>
// 任务类型定义
typedef struct _myTask
{
struct _coefficient
{
unsigned char a;
unsigned char b;
unsigned char c;
} co;
struct _variable
{
unsigned int x;
unsigned long y;
} v;
} myTask;
// 线程常量定义
#define THREAD_OVER -1 // 线程结束
#define THREAD_NOTOVER 0 // 线程未结束
// 功能:任务线程
// 参数:Task: myTask *类型,任务
// Process: unsigned char * 类型,线程指针
// 返回:char 类型
// 0: 线程未结束
// -1: 线程结束
// 备注:
char myThread(myTask *Task, unsigned char *Process)
{
unsigned int z;
char ret = 0;
switch(*Process)
{
case 0:
Task->v.x = rand(); // 获取随机数
Task->v.y = 3;
break;
case 1:
Task->v.y *= Task->v.x;
Task->v.y *= Task->v.x;
break;
case 2:
z = 7 * Task->co.a * Task->co.b * Task->v.x;
Task->v.y += z;
break;
case 3:
z = Task->co.c;
Task->v.y += z;
}
(*Process)++;
if(*Process>4)
{
ret = -1; // 线程结束
*Process = 0;
}
return ret;
}
// 任务定义
myTask Task1, Task2, Task3;
// 功能:任务初始化
// 参数:Task: myTask *类型,任务
// a, b, c:unsigned char 类型,方程式系数
// 返回:无
// 备注:
void InitTask(myTask *Task, unsigned char a, unsigned char b, unsigned char c)
{
Task->co.a = a;
Task->co.b = b;
Task->co.c = c;
}
main(void)
{
// OS变量定义区
// 任务1 OS变量
bit isTask_1_Living = 1;
unsigned char Task1_Thread_Process = 0;
// 任务2 OS变量
bit isTask_2_Living = 1;
unsigned char Task2_Thread_Process = 0;
// 任务3 OS变量
unsigned char isTask_3_Living = 5;
unsigned char Task3_Thread_Process = 0;
// 初始化任务
InitTask(&Task1, 9, 7, 18);
InitTask(&Task2, 4, 13, 16);
InitTask(&Task3, 2, 24, 3);
while(1)
{
if(isTask_1_Living) // 如果任务1活着
{
isTask_1_Living = !myThread(&Task1, &Task1_Thread_Process);
}
if(isTask_2_Living) // 如果任务2活着
{
isTask_2_Living = !myThread(&Task2, &Task2_Thread_Process);
}
if(isTask_3_Living) // 如果任务3活着
{
isTask_3_Living += myThread(&Task3, &Task3_Thread_Process);
}
}
}