UCOSII 使用叫做事件控制块(ECB)的数据结构来描述诸如信号量、邮箱(消息邮箱)和消息队列这些事件
#define OS_EVENT_EN (((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u) || (OS_SEM_EN > 0u) || (OS_MUTEX_EN > 0u))
事件控制块类型定义:
typedef struct os_event { INT8U OSEventType; /* Type of event control block (see OS_EVENT_TYPE_xxxx) */ void *OSEventPtr; /* Pointer to message or queue structure */ INT16U OSEventCnt; /* Semaphore Count (not used if other EVENT type) */ OS_PRIO OSEventGrp; /* Group corresponding to tasks waiting for event to occur */ OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; /* List of tasks waiting for event to occur */ #if OS_EVENT_NAME_EN > 0u INT8U *OSEventName; #endif } OS_EVENT;
消息邮箱:
向邮箱发送消息函数:INT8U OSMboxPost (OS_EVENT *pevent, void *pmsg)其中 pevent 为消息邮箱的指针, msg 为消息指针
该函数先检查消息邮箱结构体成员指针变量OSEventPtr是否为0,若为0,则说明消息邮箱为空,为其赋值pevent->OSEventPtr = pmsg;否则返回对应错误代码。
请求邮箱函数:void *OSMboxPend (OS_EVENT *pevent, INT16U timeout,INT8U *err) 其中 pevent 为请求邮箱指针, timeout 为等待时限, err 为错误信息。
这个函数的主要作用就是查看邮箱指针 OSEventPtr 是否为 NULL,如果不是 NULL 就把邮箱中的消息指针返回给调用函数的任务,然后清空邮箱pevent->OSEventPtr = (void *)0;同时用 OS_NO_ERR 通过函数的参数 err 通知任务获取消息成功。
如果邮箱指针OSEventPtr 是 NULL,则使任务进入等待状态,并引发一次任务调度
创建消息邮箱:OS_EVENT *OSMboxCreate (void *pmsg),
例如:
msg_key=OSMboxCreate((void*)0); //创建邮箱并初始化为空
如果指针不为空,建立的消息邮箱将含有消息
消息队列:
与邮箱相比,消息队列在OS_EVENT结构基础之上添加了一循环队列,可以同时容纳多个消息,而邮箱只能容纳一个。因此,可以将消息队列看作同时接收多条消息的邮箱。
队列控制块类型定义:
typedef struct os_q { /* QUEUE CONTROL BLOCK */ struct os_q *OSQPtr; /* Link to next queue control block in list of free blocks */ void **OSQStart; /* Pointer to start of queue data */ void **OSQEnd; /* Pointer to end of queue data */ void **OSQIn; /* Pointer to where next message will be inserted in the Q */ void **OSQOut; /* Pointer to where next message will be extracted from the Q */ INT16U OSQSize; /* Size of queue (maximum number of entries) */ INT16U OSQEntries; /* Current number of entries in the queue */ } OS_Q;
OSQSize:消息指针数组的大小
OSQEntries:消息指针数组中当前存放的消息指针数量(对应消息数量)
1) 创建消息队列函数
创建一个消息队列首先需要定义一指针数组,然后把各个消息数据缓冲区的首地址存
入这个数组中,然后再调用函数 OSQCreate 来创建消息队列。创建消息队列函数 OSQCreate
的原型为: OS_EVENT *OSQCreate(void**start,INT16U size)。其中, start 为存放消息缓冲
区指针数组的地址, size 为该数组大小。该函数的返回值为消息队列指针。
2) 请求消息队列函数
请求消息队列的目的是为了从消息队列中获取消息。任务请求消息队列需要调用函数
OSQPend,该函数原型为: void*OSQPend(OS_EVENT*pevent,INT16U timeout,INT8U *err)。
其中, pevent 为所请求的消息队列的指针, timeout 为任务等待时限, err 为错误信息。
下面是函数的部分关键代码:
pq = (OS_Q *)pevent->OSEventPtr; /* Point at queue control block */ if (pq->OSQEntries > 0u) { /* See if any messages in the queue */ pmsg = *pq->OSQOut++; /* Yes, extract oldest message from the queue */ pq->OSQEntries--; /* Update the number of entries in the queue */ if (pq->OSQOut == pq->OSQEnd) { /* Wrap OUT pointer if we are at the end of the queue */ pq->OSQOut = pq->OSQStart; } OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (pmsg); /* Return message received */ }
3) 向消息队列发送消息函数
任务可以通过调用函数 OSQPost 或 OSQPostFront 两个函数来向消息队列发送消息。函数 OSQPost 以 FIFO( 先进先出)的方式组织消息队列,函数 OSQPostFront 以 LIFO(后
进先出)的方式组织消息队列。这两个函数的原型分别为: INT8U OSQPost(OS_EVENT*pevent,void *msg)和 INT8U OSQPost(OS_EVENT*pevent,void*msg)。
其中, pevent 为消息队列的指针, msg 为待发消息的指针。
pq = (OS_Q *)pevent->OSEventPtr; /* Point to queue control block */ if (pq->OSQEntries >= pq->OSQSize) { /* Make sure queue is not full */ OS_EXIT_CRITICAL(); return (OS_ERR_Q_FULL); } *pq->OSQIn++ = pmsg; /* Insert message into queue */ pq->OSQEntries++; /* Update the nbr of entries in the queue */ if (pq->OSQIn == pq->OSQEnd) { /* Wrap IN ptr if we are at end of queue */ pq->OSQIn = pq->OSQStart; }
注:消息队列用作消息缓冲区是明智的(适合于进程通信),可要是用它来作为接收批量数据的数据缓冲区就不行了,因为ucos中的消息队列每次只能取出一条消息和每次只能放入一条消息。