UCOSIII---消息传递

UCOSIII任务之间通信

一个任务或者中断服务程序有时候需要和另一个任务交流信息,这个信息传递的过程称为任务间的通信。任务间的信息传递可以通过两种途径来实现:一是通过全局变量,二是通过发布消息

使用全局变量的时候,每个任务或者中断服务程序都必须保证其对全局变量的独占访问(通常的解决办法:关中断、临界区、信号量)。消息也可以通过消息队列作为中介发布给任务。

消息

一则消息包括几个部分:指向数据的指针,表明数据长度的变量和记录消息发布时刻的时间戳。指针指向的可以是一块数据区或者一个函数。显然,发布方和接收方需要对数据的内容和含义达成约定。换句话说,消息的接收方需要知道所接收的消息的含义才能对其进行处理。

消息的内容必须一直保持可见性,因为发布数据采用的是引用传递,是指针传递而不是值传递。也就是说,发布的数据本身并不产生拷贝。

消息队列

消息队列是一种由用户程序分配的内核对象。用户可以分配任意数量的消息队列,唯一的限制就是可用的ARM区的容量

在UCOSII中有消息邮箱和消息队列,但是在UCOSIII中只有消息队列。其实只能容纳一个消息的消息队列,可以看成是消息邮箱。消息队列是由用户创建的内核对[象,数量不限制,下图展示了用户可以对消息队列进行的操作:
在这里插入图片描述
从上图中可以看出,中断服务程序只能使用OSQPost()函数!

对消息队列的读取方式采用先进先出(fifo)的方式。在UCOSIII中,也可以采用后进先出(LIFO)的方式发布消息。当任务或者中断服务程序需要向一个任务发布一则紧急的消息时,后进先出的机制就非常有用了。使用后进先出的方式,发布的消息会绕过所有其他已经位于消息队列中的消息优先传递给任务。消息队列的容量可以在运行时进行配置。

在这里插入图片描述

消息队列的结构

UCOSIII中消息队列有关的结构体有三个,其关系如下:
在这里插入图片描述

OS_Q结构体

struct  os_q {                                              /* Message Queue                                          */
                                                            /* ------------------ GENERIC  MEMBERS ------------------ */
    OS_OBJ_TYPE          Type;                              /* Should be set to OS_OBJ_TYPE_Q                         */
    CPU_CHAR            *NamePtr;                           /* Pointer to Message Queue Name (NUL terminated ASCII)   */
    OS_PEND_LIST         PendList;                          /* List of tasks waiting on message queue                 */
#if OS_CFG_DBG_EN > 0u
    OS_Q                *DbgPrevPtr;
    OS_Q                *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
                                                            /* ------------------ SPECIFIC MEMBERS ------------------ */
    OS_MSG_Q             MsgQ;                              /* 消息队列    */
};

OS_MSG_Q结构体

struct  os_msg_q {                                          /* OS_MSG_Q                                               */
    OS_MSG              *InPtr;                             /* 指向消息队列的头 */
    OS_MSG              *OutPtr;                            /* 指向消息队列尾 */
    OS_MSG_QTY           NbrEntriesSize;                    /* 消息队列最大长度   */
    OS_MSG_QTY           NbrEntries;                        /* 消息队列当前长度   */
    OS_MSG_QTY           NbrEntriesMax;                     /* 消息队列历史中最长的的长度    */
};

这个结构体才算是消息队列的本体了,它由一个指向消息队列头的指针、指向消息队列尾的指针、消息队列中消息的最大个数、当前个数等变量组成。

OS_MSG结构体

struct  os_msg {                                            /* MESSAGE CONTROL BLOCK                                  */
    OS_MSG              *NextPtr;                           /* 指向下一个消息的指针   */
    void                *MsgPtr;                            /* 消息内容    */
    OS_MSG_SIZE          MsgSize;                           /* 消息大小   */
    CPU_TS               MsgTS;                             /* 时间戳    */
};

这个结构体才算是消息队列中的消息的本体了。上文提到:消息一般包含:指向数据的指针,表明数据长度的变量和记录消息发布时刻的时间戳。

消息队列的API函数

在这里插入图片描述

如何创建消息队列

OSQCreate()函数用来创建一个消息队列,消息队列使得任务或者中断服务程序可以向一个或者多个任务发送消息。

void  OSQCreate (OS_Q        *p_q,                            //指向一个消息队列
                 CPU_CHAR    *p_name,                        //消息队列的名字
                 OS_MSG_QTY   max_qty,                        //指定消息队列的长度,必须大于0
                 OS_ERR      *p_err)
 
{
    CPU_SR_ALLOC();
 
    OS_CRITICAL_ENTER();
    p_q->Type    = OS_OBJ_TYPE_Q;                           /* Mark the data structure as a message queue             */
    p_q->NamePtr = p_name;
    OS_MsgQInit(&p_q->MsgQ,                                 /* Initialize the queue                                   */
                max_qty);
    OS_PendListInit(&p_q->PendList);                        /* Initialize the waiting list                            */
 
    OSQQty++;                                               /* One more queue created                                 */
 
    OS_CRITICAL_EXIT_NO_SCHED();
   *p_err = OS_ERR_NONE;
}

max_qty:指定消息队列的长度,必须大于0。

等待消息队列

当一个任务想要从消息队列中接收一个消息的话就需要使用函数OSQPend()。

void  *OSQPend (OS_Q         *p_q,                                //指向一个消息队列
                OS_TICK       timeout,                            //等待消息的超时时间
                OS_OPT        opt,                            //用来选择是否使用阻塞模式
                OS_MSG_SIZE  *p_msg_size,                    //指向一个变量用来表示接收到的消息长度(字节数)
                CPU_TS       *p_ts,                        //指向一个时间戳
                OS_ERR       *p_err)
{
    OS_PEND_DATA  pend_data;
    void         *p_void;
    CPU_SR_ALLOC();
 
    if (p_ts != (CPU_TS *)0) {
       *p_ts  = (CPU_TS  )0;                                /* Initialize the returned timestamp                      */
    }
 
    CPU_CRITICAL_ENTER();
    p_void = OS_MsgQGet(&p_q->MsgQ,                         /* Any message waiting in the message queue?              */
                        p_msg_size,
                        p_ts,
                        p_err);
    if (*p_err == OS_ERR_NONE) {
        CPU_CRITICAL_EXIT();
        return (p_void);                                    /* Yes, Return message received                           */
    }
 
    if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {    /* Caller wants to block if not available?                */
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_PEND_WOULD_BLOCK;                    /* No                                                     */
        return ((void *)0);
    } else {
        if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {    /* Can't pend when the scheduler is locked                */
            CPU_CRITICAL_EXIT();
           *p_err = OS_ERR_SCHED_LOCKED;
            return ((void *)0);
        }
    }
 
    OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();                  /* Lock the scheduler/re-enable interrupts                */
    OS_Pend(&pend_data,                                     /* Block task pending on Message Queue                    */
            (OS_PEND_OBJ *)((void *)p_q),
            OS_TASK_PEND_ON_Q,
            timeout);
    OS_CRITICAL_EXIT_NO_SCHED();
 
    OSSched();                                              /* Find the next highest priority task ready to run       */
 
    CPU_CRITICAL_ENTER();
    switch (OSTCBCurPtr->PendStatus) {
        case OS_STATUS_PEND_OK:                             /* Extract message from TCB (Put there by Post)           */
             p_void     = OSTCBCurPtr->MsgPtr;
            *p_msg_size = OSTCBCurPtr->MsgSize;
             if (p_ts  != (CPU_TS *)0) {
                *p_ts   =  OSTCBCurPtr->TS;
             }
            *p_err      = OS_ERR_NONE;
             break;
 
        case OS_STATUS_PEND_ABORT:                          /* Indicate that we aborted                               */
             p_void     = (void      *)0;
            *p_msg_size = (OS_MSG_SIZE)0;
             if (p_ts  != (CPU_TS *)0) {
                *p_ts   =  OSTCBCurPtr->TS;
             }
            *p_err      = OS_ERR_PEND_ABORT;
             break;
 
        case OS_STATUS_PEND_TIMEOUT:                        /* Indicate that we didn't get event within TO            */
             p_void     = (void      *)0;
            *p_msg_size = (OS_MSG_SIZE)0;
             if (p_ts  != (CPU_TS *)0) {
                *p_ts   = (CPU_TS  )0;
             }
            *p_err      = OS_ERR_TIMEOUT;
             break;
 
        case OS_STATUS_PEND_DEL:                            /* Indicate that object pended on has been deleted        */
             p_void     = (void      *)0;
            *p_msg_size = (OS_MSG_SIZE)0;
             if (p_ts  != (CPU_TS *)0) {
                *p_ts   =  OSTCBCurPtr->TS;
             }
            *p_err      = OS_ERR_OBJ_DEL;
             break;
 
        default:
             p_void     = (void      *)0;
            *p_msg_size = (OS_MSG_SIZE)0;
            *p_err      = OS_ERR_STATUS_INVALID;
             break;
    }
    CPU_CRITICAL_EXIT();
    return (p_void);
}

timeout:等待消息的超时时间,如果在指定的时间没有接收到消息的话,任务就会被唤醒,接着运行。这个参数也可以设置为0,表示任务将一直等待下去,直到接收到消息。

opt:用来选择是否使用阻塞模式
OS_OPT_PEND_BLOCKING:如果没有任何消息存在的话就阻塞任务,一直等待,直到接收到消息。OS_OPT_PEND_NON_BLOCKING:如果消息队列没有任何消息的话任务就直接返回。

**p_ts:指向一个时间戳,**表明什么时候接收到消息。如果这个指针被赋值为NULL的话,说明用户没有要求时间戳。

向消息队列发送消息

可以通过函数OSQPost()向消息队列发送消息,如果消息队列是满的,则函数OSQPost()就会立刻返回,并且返回一个特定的错误代码。

void  OSQPost (OS_Q         *p_q,                        //指向一个消息队列
               void         *p_void,                        //指向实际发送的内容
               OS_MSG_SIZE   msg_size,                    //设定消息的大小,单位为字节数
               OS_OPT        opt,                        //用来选择消息发送操作的类型
               OS_ERR       *p_err)
{
    CPU_TS  ts;
 
    ts = OS_TS_GET();                                       /* Get timestamp                                          */
 
    OS_QPost(p_q,
             p_void,
             msg_size,
             opt,
             ts,
             p_err);
}

p_void:指向实际发送的内容,p_void是一个执行void类型的指针,其具体含义由用户程序的决定

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UCOS III中的消息队列是一种用于任务间通信的数据结构,可以在任务和任务之间,中断和任务之间传递信息。当队列中没有有效数据时,读取消息的任务将被阻塞并等待有效数据的到来。用户可以指定阻塞的任务时长。一旦队列中有消息到达,被阻塞的任务将被唤醒并处理新消息。如果等待的时间超过了指定的阻塞时间,即使队列中仍然没有有效数据,任务也会从阻塞状态转为就绪状态。当有多个消息发送到消息队列时,通常先进入队列的消息会先传递给任务,遵循“先进先出”的原则。 UCOS III中的消息队列的使用方法包括以下几个步骤: 1. 定义消息队列结构体:UCOS III中的消息队列使用结构体os_msg_q来表示,其中包含了队列中当前的消息个数NbrEntries等信息。 2. 初始化消息队列:在使用消息队列之前,需要先对其进行初始化。可以使用函数os_msg_q_create来创建一个消息队列,并指定队列的大小和每个消息的长度。 3. 发送消息消息队列:使用函数os_msg_q_post来向消息队列发送消息,将消息的指针作为参数传入。发送消息后,队列中的消息个数NbrEntries会自动加1。 4. 接收消息消息队列:使用函数os_msg_q_pend来从消息队列接收消息,函数会阻塞当前任务直到有消息到达队列。一旦有消息到达,将会从队列中取出最早的消息,并返回消息的指针。同时,队列中的消息个数NbrEntries会自动减1。 以上是UCOS III消息队列的基本用法,通过定义消息队列结构体、初始化消息队列、发送消息和接收消息,可以实现任务间的信息传递。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [UCOS-III学习(四)——消息队列](https://blog.csdn.net/weixin_43795667/article/details/121526352)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [UCOSIII-消息队列](https://blog.csdn.net/m0_52992429/article/details/120995633)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值