μC/OS-Ⅱ之任务设计

来源:互联网 发布:数据存储架构图 编辑:程序博客网 时间:2024/04/29 07:09

在用户任务函数中,必须包含至少一次对操作系统服务函数的调用;否则比其优先级低的任务将无法得到运行机会。这是用户任务函数与普通函数的明显区别。

任务函数的结构按任务的执行方式可以分为三类:单次执行类、周期执行类事件触发类

单次执行类:任务在创建后只执行一次,执行结束后即自行删除;采用创建任务函数来启动。

void MyTask(void *pdata)

{

    进行准备工作的代码; //完成各项准备工作:定义、初始化变量或某些设备,也可能空缺

    任务实体代码;       //完成该任务的具体功能:对若干系统函数的调用,除若干临界段代码(中断被关闭)外,任务的其他代码均可以被中断,以保证优先级高的就绪任务能及时运行

    调用任务删除函数;   //调用OSTaskDel(OS_PRIO_SELF)

}

注:启动任务是单次执行任务,优先级最高(为了保证能够连续运行),但启动任务不是用户系统的实质任务,又占用高优先级资源和任务资源,故不常用。更常用的是将“启动任务”所完成的操作交给一个用户系统的实质任务来完成。例:

渭C/OS-Ⅱ之任务设计1:单次执行类的任务使用启动任务:

void main(void)

{

   OSInit();  //初始化OS

   OSTaskCreate(TaskStart,(void *)0,&TaskStartStk[TASK_STK_SIZE-1],1);//创建启动任务

   OSStart(); //创建OS,开始对任务进行调度管理

}

void TaskStart(void *pdata) //启动任务

{

   pdata = pdata;

   系统硬件初始化;        //时钟系统、中断系统和外设等

   创建各个任务;          //如键盘、显示、采样、数据处理、打印等

   创建各种通信工具;      //如信号量、消息邮箱、消息队列等

   OSTaskDel(OS_PRIO_SELF);//删除

}

渭C/OS-Ⅱ之任务设计1:单次执行类的任务用户任务代替启动任务:

void main(void)

{

   OSInit();  //初始化OS

   OSTaskCreate(TaskUser1,(void *)0,&TaskUser1Stk[TASK_STK_SIZE-1],1);//创建任务1

   OSStart(); //创建OS,开始对任务进行调度管理

}

void TaskUser1(void *pdata) //用户任务1

{

   pdata = pdata;

   系统硬件初始化;        //时钟系统、中断系统和外设等

   创建各个任务;          //如键盘、显示、采样、数据处理、打印等

   创建各种通信工具;      //如信号量、消息邮箱、消息队列等

   用户任务1本身的代码;

}

采用创建任务的方式来启动任务,可省略用通信手段触发任务的麻烦,还可通过*pata来传递原始参数,使每次启动任务时可有不同的工作状态。OSTaskCreate(...,&baud,...,...).

但,需要对任务控制块链表和任务就绪表进行操作,比较耗时,故只适用于实时性要求不高的任务(键盘),自我删除后也会出现许多问题:资源、内存的释放问题;通信双方信号相应、挂起问题等。适合创建任务的方式来启动任务,通常是“孤立任务”,它们不和其他任务进行通信(ISR除外),只使用共享资源来获取信息和输出信息。若不满足这个条件,则应采用周期性执行和事件触发执行两种任务函数机构,并在启动时就创建好。

μC/OS-Ⅱ之任务设计:周期性执行的任务

此类任务在创建后按一个固定的周期来执行。

void MyTask(void *pdata)
{

   进行准备工作的代码;

   while(1)            //无限循环

   {

     任务实体代码;

     调用系统延时函数;//调用OSTimeDly()或OSTimeDlyHMSM()

   }

}

 调用系统延时函数时,把CPU的控制权主动交给OS,使自己挂起,再由OS来启动其他已经就绪的任务。当延时时间到后,重新进入就绪状态,通常能够很快获得运行权。

 

当任务执行周期远大于系统时钟节拍时,任务执行周期的相对误差比较小当任务执行周期只有几个时钟节拍时,相邻两次执行的间隔时间抖动不能忽视,任务的执行周期的相对误差比较大,只适用于对周期稳定性要求不高的任务(键盘);当任务执行周期只有一个时钟节拍时,可将该任务的功能放到OSTimeTickHook()(时钟节拍函数中的钩子函数)中去执行;当任务执行周期小于一个时钟节拍或者不是时钟节拍的整数倍时,将无法使用延时函数对其进行周期控制,而只能采用独立于OS的定时中断来触发采用独立定时器出发的任务具有很高的周期稳定性

周期性执行的任务函数编程比较单纯,只要创建一次,就能周期运行。在实际应用中,很多任务都具有周期性,它们的任务函数都使用这种结构(键盘)。

C/OS-Ⅱ之任务设计:事件触发执行的任务

此类任务在创建后,虽然很快可以获得运行权,但实体代码的执行需要等待某种事件的发生,在相关事件发生之前,则被OS挂起。相关事件发生一次,该任务实体代码就执行一次,故该类型任务称为事件触发执行的任务。例:
void MyTask(void *pdata)
{
   进行准备工作的代码;
   while(1)
   {
      调用获取事件的函数;//如等待信号量和等待邮箱中的消息等(另一个任务或ISR发出的)
      任务实体代码;
   }
}
在获得这个信息之前处于等待状态(挂起状态),当另一个任务(或ISR)发出相关信息时(调用了OS提供的通信函数),OS就使该任务进入就绪状态,并且通过任务调度,使任务的实体代码获得运行权,完成该任务的实际功能。
OSSemPost(Sem);//发送信号量
OSSemPend(Sem,0,&err);//等待信号量
若在触发任务时还需要传送参数,就可以采用发送消息的方法:
OSMboxPost(Mybox,&baud);//发送消息(波特率)
baud = *(INT16U *)OSMboxPend(Mybox,0,&err);//等待消息。获取波特率
当触发条件为“时间间隔”时(定时器中断触发),该任务就具有周期性。这种任务 函数结构适用于执行周期小于一个时钟节拍或者不是时钟节拍整数倍的周期性任务,即周期性任务也能用事件触发执行的任务函数来实现。定时中断负责按预定的时 间间隔准确的发出信号量,被关联的任务总是处于等待信号量的状态下,每得一次信号量就执行一次。这种方式具有比较准确的周期。



0 0