前面写那个菜单程序时,用了两个函数指针,防止在进行菜单操作时系统中有任务进行,所以增加了一个排队的指针。不过如果我们进行菜单操作时已经有程序在那里排队了该咋办呢?呵呵,是不是有些没完没了了。不过我们的程序能够多准备一步总归是好的。
这里的任务管理可不是像操作系统那样进行任务调度,只是一个非常简单(简单的到了简陋的地步)的任务队列的处理,可以将一系列将要处理的函数放置在一个任务队列里,然后主程序一个一个的调用执行。而且,将程序调用安排在主程序中,这样任务函数就可以像普通函数那样写就可以了,不用关心当前任务队列的情况。
这个功能的实现仍然是基于一个任务链表进行的。链表中的数据结构定义如下:
typedef struct _Function_Point{
unsigned char Next_Function;
void (*Function_Addr) ();
} Function_Point;
其中Next_Function成员变量指向队列中下一个单元数据的索引,而Function_Addr变量则保存该队列位置的函数指针。
在建立数组时,将最后一个数据的Next_Function成员变量指向第一个的索引,从而构成一个单向环形链表。
使用时,在程序中定义两个下标指针Function_Point_Read和Function_Point_Write。前一个用于指向当前需要执行的任务,后一个指向当前队列中第一个空白位置,也就是任务队列队尾的下一个数据。
进行添加任务时,需要判定Function_Point_Write位置的数据Function_Addr变量是否为0,如果为0,则将任务添加到队列同时写入指针加1,指向下一个空白位置。如果当前Function_Addr变量不为0则表示当前任务队列已满,只好忽略了。
执行任务时,主程序中依然只需要一个无限循环,如下:
while(1)
{if (Menu_Function[Function_Point_Read].Function_Addr !=0)
{
Menu_Function[Function_Point_Read].Function_Addr ();
Menu_Function[Function_Point_Read].Function_Addr=0;
Function_Point_Read=Menu_Function[Function_Point_Read].Next_Function;
}
}
程序的功能是,如果当前指向的数据中Function_Addr变量不为0,则表示当前任务队列中有待处理的任务,执行它!
执行完毕后,将执行完的任务地址清零,同时将Function_Point_Read指向下一个变量,在下一个循环周期中执行重复的判断、执行并清理等工作。
由于程序起始时Function_Point_Read和Function_Point_Write的值相等,而且,队列中每添加一个任务,Function_Point_Write会自动指向下一个位置;而每执行一个任务(同时将该位置的任务指针清零)Function_Point_Read也会自动指向下一个位置。所以,当系统中没有任务运行时,Function_Point_Read和Function_Point_Write的值总是相等的,因而每次写入队列中的新值都会启动任务队列的执行。就好像前面一个人挖坑,后面一个人种树,当种树的追上挖坑的人的时候,工作会停止,前面再挖一个坑,工作又可以继续。当然,前提要是种树的不会偷懒。我觉得我们的处理器是不会偷懒的,你说呢?