继续上一节的讲解,uCOS-II通过OSTaskCreate()和OSTaskCreateExt()来创建任务,后者相当于前者的一些扩展。
下面对OSTaskCreate()的源码进行讲解:
INT8U OSTaskCreate( void (*task)(void *pd), //指向任务的指针
void * pdata, //传递给任务的参数
OS_STK * ptos, //指向任务堆栈栈顶指针
INT8U prio //任务的优先级
)
{
#if OS_CRITCAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
void *psp;
INT8U err;
if(prio > OS_LOWEST_PRIO) //检测任务优先级是否合法
{
return (OS_PRIO_INVALID);
}
OS_ENTER_CRITICAL();
if(OSTCBPrioTbl[prio] == (OS_TCB *)0) //确认优先级未被使用
{
OSTCBPrioTbl[prio] = (OS_TCB*) 1; //保留优先级
OS_EXIT_CRITICAL();
psp = (void *)OSTaskStkInit( task,
pdata,
ptos,
0
); //初始化任务堆栈
err = OSTCBInit( prio,
psp,
(void *)0,0,0,
(void *)0,0
); //获得并初始化任务控制块
if(err == OS_NO_ERR)
{
OS_ENTER_CRITICAL();
OSTaskCtr++; //任务计数器+1
OS_EXIT_CRITICAL();
if(OSRunning)
{
OSSChed();
}
}
else
{
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = (void *)0; //放弃任务
OS_EXIT_CRITICAL();
}
return (err);
}
else
{
OS_EXIT_CRITICAL();
}
}
从上述源码我们可以看到,函数对创建任务的优先级先进行判断,确定合法后,调用OSTaskStkInit() 和 OSTCBInit()对任务堆栈和任务控制块进行初始化,初始化完成后,除了把任务计数器+1外,还要对CPU是否处于运行状态,为1则调用函数OSSched()任务调度。
创建任务的一般方法:
一般来说,任务可在调用OSStart()启动调度任务之前创建,也可在任务中创建,但是在uCOS-II中,在调用OSStart()之前,系统必须已经创建了一个任务。因此习惯上在OSStart()之前创建一个任务,并赋予这个任务最高优先级,使其成为起始任务,在起始任务中再创建其他任务。
注意:如果要使用系统提供的统计任务,则必须在起始任务中的初始化函数中调用统计任务的初始化函数。
下面是创建任务的示意性代码:
void main()
{
.....
OSInit(); //对uCOS进行初始化
.....
OSTaskCreate(TaskStart,......); //创建起始任务TaskStart
OSStart(); //开始多任务调度
}
void main()
{
..... //在这个位置安装并启动uCOS的时钟
OSStatInit(); //初始化统计任务
....... //在这个位置创建其他任务
for(;;)
{
//起始任务TaskStart的代码
}
}
//特别注意:uCOS-II不允许在中断服务中创建任务
挂起任务:
挂起任务函数OSTaskSuspend(INT8U prio)如果任务挂起自身参数为常数OS_PRIO_SELF,从下面的流程图可以看出,首先判断将要挂起的任务是否是自身,若是,则必须删除该任务在任务就绪表中的就绪标志,并在任务控制块成员OSTCBStat中做挂起记录,引发一次任务调度,以使CPU去运行就绪的其他任务。若果不是任务自身,那么只要删除任务就绪表中被挂起任务的就绪状态,并在任务控制块成员OSTCBStat中做挂起记录。
恢复任务:
恢复任务函数INT8U OSTaskResume(INT8U prio). 从下面的流程图可以看出函数在判断任务确实是一个已存在的挂起任务,同时它又不是一个等待任务(任务控制块成员OSTCBDly =0)时,就会清除任务控制块成员OSTCBStat中的挂起记录并使任务就绪,最后调用调度器OSSched()进行任务调度,并返回函数调用成功的信息OS_NO_ERR。
任务优先级的修改:
没啥要说的,知道去调用这个函数就可以了
INT8U OSTaskChangePrio(
INT8U oldprio; //任务现在的优先级别
INT8U newprio; //要改为的优先级别
);
任务的删除;
所谓删除,不是真的删除,是将该任务置于睡眠状态。具体操作就是把删除任务的任务控制块从任务控制块链表中删除,并归还给空白控制块链表,然后在任务就绪表中把该任务的就绪状态置为0,这样就不会被调度器调用。
通过调用OSTaskDel(INT8U prio)来删除任务自身或者除了空闲任务之外的其他任务,有的人说了,我看你之前写的代码也没看到你你创建空闲任务,这个是系统在你调用函数OSInit()自动完成的。删除自身参数为OS_PRIO_SELF。
我们知道任务有时候会占用一些动态分配的内存或者信号量之类的资源。这就涉及到内存管理了,最严重直接后果就是系统死机。所以uCOS-II提出,删除任务请求的任务只负责提出删除请求,具体删除内容由被删除任务自己完成。
显然想要使删除任务请求的任务和被删除任务之前能够像上述方式执行,双方必须存在通信。uCOS-II利用被删除任务的任务控制块成员OSTCBDelReq作为请求删除方的被删除方的联络信号,同时提供一个双方都能调用的函数--请求删除任务函数OSTCBDelReq(INT8U prio),这样双方就都能使用这个函数来访问OSTCNDelReq这个信号,从而可以根据这个信号的状态来决定各自的行为。
提出删除任务请求的任务在调用这个函数是,参数为被删除任务的优先级别,被删除任务在调用这个函数是,参数是OS_PRIO_SELF。
查询任务的信息:
没啥好说的,调用这个函数成功返回OS_NO_ERR,查询的信息存放在OS_TCB结构类型的变量中
INT8U OSTaskQuery(
INT8U prio; //待查询任务的优先级别
OS_TCB *pdata; //存储任务信息的结构
);
uCOS-II的初始化和任务的启动
在使用uCOS-II的所有服务之前,必须调用uCOS-II的初始化函数OSInit(),对自身环境进行初始化。
函数OSInit()将对uCOS-II的所以噗全局变量和数据结构进行初始化,同时创建空闲任务OSTaskidle,并辅之以最低优先级和永远就绪的状态,如要使用统计任务(常数OS_TASK_STAT_EN = 1),则OSInit()还要以优先级别为OS_LOWEST_PRIO-1来创建统计任务。
初始化OSInit()在对数据结构进行初始化时,主要创建包括空任务控制块链表在内的5个空数据缓冲区。同时为了可以快速查询任务控制块链表中的各个元素,OSInit()还要创建一个数组OSTCBPrioTbl[OS_LOWEST+1],在这个数据中,按任务的优先级别顺序把任务控制块的指针存放在对应的元素中。
uCOS-II启动:
uCOS-II进行任务的管理是从调用启动任务函数OSStart()开始的,当然前提条件是系统至少创建了一个任务。源代码如下:
void OSStart (void)
{
INT8U y;
INT8U x;
if(OSRunning == FALSE)
{
y = OSUnMapTbl[OSRdyGrp];
x = OSUnMapTbl[OSRdyTbl[y]];
OSPrioHighRdy = (INT8U) ((y<<3)+x);
OSPrioCur = OSPrioHighRdy;
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
OSTCBCur = OSTCBHighRdy;
OSStartHighRdy();
}
}
OSStartHighRdy()在多任务系统启动函数OSStart()中调用。完成的功能是:设置系统运行标志OSRunning = TRUE,将就绪表中最高优先级任务的栈指针Load到SP中,并强制中断返回。这样就绪的最高优先级任务就如同从中断返回到运行状态一样,使整个系统得以运转。
最近比较忙,我还是个实习生,就负责一个项目的开发,很多东西都要去协调,最近硬件出了问题,硬件部门非说我们软件部这边有问题,检查了半个月,最终将硬件打回去了,重新设计。更新的就比较慢了。