ucos消息队列

    如果把邮箱比作信号量的升级版,那消息队列就是邮箱的升级版,邮箱可以实现从一个任务向另一个任务发送一个指针变量,消息队列则可以实现从一个任务向

另一个任务发送多个指针变量,而且每个指针指向的数据结构变量也可以有不同。

   使用消息队列需要注意的恩地方是:一个任务或者中断服务子程序可以调用OSQPost(),OSQPostFront(),OSQFlush(),或者OSQAccept函数,但是只有任务

可以调用OSQPend()和OSQQQuery函数。

   我们知道,每个事件有一个对应事件控制块,用于记录有关这个事件的消息,消息队列也不理瓦,一个消息队列对应一个队列控制块。

   相对而言,邮箱而言,ucosii定义了一个数据结构来存储消息队列的信息,

typedef struct os_q{

  struct os_q *OSQPtr;

   void **OSQStart;

   void **OSQEnd;

   void **OSQOut;

   INT16U OSQSize;

   INT16U  OSQEtries;

}OS_Q;

OSQPtr在空闲队列控制块中链接所有的队列控制块,一旦建立了消息队列,该域就不再有用了

OSQStart是指向消息队列的指针数组的起始地址的指针,用户应用程序在使用消息队列之前必须先定义该数组。

OSQEnd是指向消息队列结束单元的下一个地址的指针,该指针使得消息队列构成成一个循环的缓冲区。

OSQIn 是指向消息队列中插入下一条消息的位置的指针,当OSQIn和OSQEnd相等时,OSQIn被调整指向消息队列的起始单元。

OSQOut是指向消息队列中下一个取出消息的位置的指针,当OSQOut和OSQEnd相等时,OSQOut被调整指向消息队列的起始单元。

OSQSize是消息队列中总的单元数,该值是在建立消息队列时由用户应用程序决定的,该值最大可以是65535

OSQEntries是消息队列中当前的消息数量,当消息队列是空的时,该值为0,当消息队列满了以后,改制和OSQSize值一样,在消息uil刚建立时,该值为0.

ucosii提供了7个消息队列进行操作的函数

1.建立一个消息队列,OSQCreat();

2.等待一个消息队列中的消息 OSQPend();

3.向消息队列发送一个消息(FIFO)OSQPost();

4.向消息队列发送一个消息(后进先出FIFO),OSQPostFront();

5.无等待地从一个消息队列中取得消息OSQAccept()

6.清空一个消息队列OSQFlush();

7.查询一个消息队列的状态OSQQuery();

消息队列最根本的部分时一个缓冲区,其中的每个单元包含一个指针,队列未满时,OSQIn指向下一个存放消息的地址单元,如果队列已满,OSQIn则与OSQOut指向同一单元,如果在OSQIn指向的单元插入新的消息的指针,就构成FIFO队列,相反,如果在OSQOut指向的单元的下一个单元插入新的指针,就构成LIFO队列。当OSQEntries和OSQSize相等,说明队列已满,消息指针总是从OSQOut指向的单元取出,指针OSQStart和OSQEnd定义了消息指针数组的头尾,以便在OSQIn和OSQOut到达队列的边缘时,进行编辑检查和必要的调整,实现循环功能。


消息队列通常可以应用一下两个地方:

1.存储外部事件:外部事件由中断收集,然后存储到队列中。

2.串口接收程序中的接收缓冲区,可以理解为消息队列。

使用一个消息队列的步骤如下:

1.建立一个指向消息数组的指针和数组的大小,该指针数组必须为void类型

     void *MyArrayOfMsg[SIZE];

2.声明一个OS_EVENT 类型的指针指向生成的队列

    OS_EVENT *QSem;

3.调用OSQcreate()函数创建消息队列,如下:

    QSem =OSQcreate(&MyArrayOfMsg[0],SIZE);

4.等待消息队列中的消息,OSQPend();

5.向消息队列发送一则消息。


建立一个消息队列,OSQCreate()的实现代码如下:

OS_EVENT *OSQCreate(void **start,INT16U size)

{

    OS_EVENT *pevent;

     OS_Q *pq;

    OS_EVENT_CRITICAL();

    pevent=OSEventFreeList;                    (1)

   if(OSEventFreeList!=(OS_EVENT*)0)

  {

                 OSEventFreeList=(OS_EVENT*)OSEventFreeList->OSEventPtr;   (2)

  }

  OS_EIXT_CRITICAL();

  if(pevent!=(OS_EVENT*)0)

 {

       OS_ENTER_CRITICAL();

       pq=OSQFreeList;               (3)

       if(OSQFreeList!=(OS_Q*)0)

        {

                 OSQFreeList=OSQFreeList->OSQPtr;

        }

      OS_EXIT_CRITICAL();

     if(pq!=(OS_Q*)0)

     {

           pq->OSQStart  =start;                               (4)

          pq->OSQEnd=  &start[SIze];

          pq->OSQIn= start;

         pq->OSQOut=start;

         pq->OSQSize=size;

         pq->OSQEntries =0;

        pevent->OSEventType=OS_EVENT_TYPE_Q;        (5)

        pevent->OSEventPtr=pq;                      (6)

        OSEventWaitListInit(pevent)                             (7)


    }

     else

       {

OS_ENTER_CRITICAL();

                  pevent->OSEventPtr=(void*)OSEventFreeList;  (8)

                  OSEventFreeList=pevent;

                  OS_EIXT_CRITICAL();

                   pevent=(OS_EVENT*)0;

      }

}

    return(pevent);              (9)

}

它和创建邮箱,创建信号量过程很相似,首先申请控制块,接着初始化这个控制块,和创建邮箱,信号量不同,创建消息队列过程

是多申请一个队列控制块

OSQCreate()首先从空闲事件控制块链表中取得一个事件控制块(1);并对剩余的空闲事件控制块列表的指针做相应的调整,

使它执行下一个空闲事件控制块(2),接着OSQCreate()函数从空闲队列控制块列表中取出一个队列控制块(3),如果空闲队列控制块是可以

就对其进行初始(4).然后该函数将事件控制块的类型设置为OS_EVENT_TYPE_Q(5),使其OSEventPtr指针指向队列控制块(6),OSQCreate还要调用OSEventWaitListInit()

向它的调用函数返回一个指向事件控制块的指针(9),该指针将在调用OSQPend(),OSQPost(),OSQPostFront(),OSQFlush(),OSQAccept和OSQQuery

等消息队列处理函数的使用,因此,该指针可以被看作是对消息队列的句柄,值得注意的是,如果此时没有空闲的事件控制块,OSQCreate函数将返回一个NULL指针,如果没有

队列控制块可以使用,为了不浪费事件控制块资源,OSQCreate函数将把刚刚取得的事件控制块反给空闲控制块列表【8】。


#define TASK_STK_SIZE    512

#define N_MESSAGES      128

OS_STK   StartTaskStk[TASK_STK_SIZE]

OS_STK   MyTaskStk[TASK_STK_SIZE]

OS_STK   YouTaskStk[TASK_STK_SIZE];

char *s_flag;  //该字符串指示哪个任务在运行

char *ss;  //存放接收到的消息指针

char *s100;//存放发送消息的指针

char *s;

char *s500;

void *MsgGrp[N_MESSAGES];//定义消息指针数组

//创消息队列,首先需要定义一个指针数组,然后把各个消息数据缓冲区的首地址存入这个数组中,最后调用函数OSQCreate()来创建消息队列

INT8U err;

INT8U y=0;

OS_EVENT *Str_Q; //定义事件控制块指针 队列的事件控制块指针,用于存放创建消息队列的指针

void MyTask(void *data)

void StartTask(void *data);

void YouTask(void *data);


void  main(void)

{

     OSInit();

     Str_Q=OSQCreate(&MsgGrp[0],N_MESSAGES);//创建消息队列

     //函数的第一个参数&MsgGrp[0]是void **start,是存放系哦啊新缓冲区指针数组的地址,它是指向指针数组的指针

    //可以用指针数组的首个元素的地址表示 N_MESSAGES是该数组的大小 返回值是消息队列的指针,Str_Q是OS_EVENT型指针

    OSTaskCreate(StartTask,(void*)0,&StartTaskStk[TASK_STK_SIZE-1],0);

      OSStart();

}

void StartTask(void *pdata)

{

#if OS_CRITICAL_METHOD==3

      OS_CPU_SR  cpu_sr;

#endif

  INT16S  key;

   pdata=pdata;

   OSStatInit();

    OSTaskCreate(MyTask,(void*)0,&MyTaskStk[TASK_STK_SIZE-1],3);

    OSTaskCreate(YouTask,(void*)0,&YouTaskStk[TASK_STK_SIZE-1],4);

   for(;;)

   {

       s_flag="The StartTask is runnig!";

      if(OSTimeGet()>100&&OSTimeGet()<500)

     {

s500="The value ofOSTIME is from 1000 to 1500NOW";

        OSQPostFront(Str_Q,s100); //发送消息,以LIFO后进先出的方式发送

         //发送消息,以LIFO后进先出的方式发送

        //第一个参数Str_Q是消息队列的指针,是OSQCreate的返回值,第二个参数s是消息指针

        s="The string belongs to which task";

        OSQPostFront(Str_Q,s);  //发送消息,以LIFO方式发送,所以如果要申请消息时,会先得到s,然后才是s100;

    }

       if(OSTimeGet()>1000&&OSTimeGet()<1500)

      {

   s500="The value of OSTIME is from 1000 to 1500 NOW";

           OSQPostFront(Str_Q,s500);

      }

       if(PC_GetKey(&key)==TRUE)

       {

                 if(key==0x1B)

                {

                             PC_DOSReturn();

               }

        }

              OSTimeDlyHMSM(0,0,1,0);

    }

}


void MyTask(void *pdata)

{

   #if OS_CRITICAL_METHOD==3

       OS_CPU_SR cpu_sr;

  #endif

    pdata=pdata;

    for(;;)

    {

            s_flag="The MyTask is running!";

              ss=OSQPend(Str_Q,0,&err);//请求消息队列,参数分别是:Str_Q为请求的消息队列的指针,第二参数为等待时间  

                                                            //0表示无限等待,&err为错误信息,返回值为队列控制块OS_Q成员OSQOut指向的消息,如果没有消息可用,在

                                                           //使调用OSQPend的任务挂起来,使之处于等待状态,并引发一次任务调度

                                                          //因为前面发送的消息是使用的是LIFO的方式,所以第一次得到的消息是上面最后发送的消息

               PC_DispStr(3,y,ss,DISP_FGND_BLACK+DISP_BGND_LIGHT_GRAY)//显示得到的消息

             PC_DispStr(0,y,"My",DISP_FGND_RED+DISP_BGND_LIGHT_GRAY);

             OSTimeDlyHMSM(0,0,1,0);

    }

}


运行的现象说明上面分析时正确的,因为当时钟节拍大于100,小于500时,会发送第一个if语句中的两个字符串

s100和s

下面运行的任务接收到并且显示,当时钟节拍数大于1000小于1500时,发送第二个if语句的字符,下面运行的、人

//接收并显示,当时钟节拍数大于1500时,就不再发送消息,下面的任务得不到就无限等待下去,所以就不再显示

从运行的现象不难可以看出,有时MyTask或YouTask运行了,但是没有得到消息而处于等待状态,使用上面的

方法很清纯地看出来任务调度和运行的关系,MyTask和YouTask是交运行的,因为延时时间相等



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值