//BY 简单的元清
//部分内容引用了其他博主的文章,对这些博主表示感谢,时间关系就不一一指出了。
//如有转载,请说明,谢谢
/*
*********************************************************************************************************
* uC/OS-II
* The Real-Time Kernel
* MESSAGE QUEUE MANAGEMENT
*
* (c) Copyright 1992-1998, Jean J. Labrosse, Plantation, FL
* All Rights Reserved
*
* V2.00
*
* File : OS_Q.C
* By : Jean J. Labrosse
*********************************************************************************************************
*/
#ifndef OS_MASTER_FILE
#include "software\includes.h"
#endif
#if OS_Q_EN && (OS_MAX_QS >= 2)
/*
*********************************************************************************************************
* LOCAL DATA TYPES
*********************************************************************************************************
*/
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;
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
static OS_Q *OSQFreeList; /* Pointer to list of free QUEUE control blocks */
static OS_Q OSQTbl[OS_MAX_QS]; /* Table of QUEUE control blocks */
/*$PAGE*/
/*
*********************************************************************************************************
* ACCEPT MESSAGE FROM QUEUE
*
* Description: This function checks the queue to see if a message is available. Unlike OSQPend(),
* OSQAccept() does not suspend the calling task if a message is not available.
*
* Arguments : pevent is a pointer to the event control block
*
* Returns : != (void *)0 is the message in the queue if one is available. The message is removed
* from the so the next time OSQAccept() is called, the queue will contain
* one less entry.
* == (void *)0 if the queue is empty
* if you passed an invalid event type
*********************************************************************************************************
*/
// 无等待地从一个消息队列中取得消息
void *OSQAccept (OS_EVENT *pevent) reentrant
{
void *msg;
OS_Q *pq;
OS_ENTER_CRITICAL();
//进入临界区
if (pevent->OSEventType != OS_EVENT_TYPE_Q) { /* Validate event block type */
//检查队列的类型是不是队列
OS_EXIT_CRITICAL();
return ((void *)0);
//如果不是就退出临界区
}
pq = pevent->OSEventPtr; /* Point at queue control block */
//指针指向队列的块
if (pq->OSQEntries != 0) { /* See if any messages in the queue */
//检查队列里面是否有东西
msg = *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;
//如果队列到头了需要把队列的指针从头开始
}
} else {
msg = (void *)0; /* Queue is empty
//没有消息
*/
}
OS_EXIT_CRITICAL();
//退出临界区
return (msg); /* Return message received (or NULL) */
}
/*$PAGE*/
/*
*********************************************************************************************************
* CREATE A MESSAGE QUEUE
*
* Description: This function creates a message queue if free event control blocks are available.
*
* Arguments : start is a pointer to the base address of the message queue storage area. The
* storage area MUST be declared as an array of pointers to 'void' as follows
*
* void *MessageStorage[size]
*
* size is the number of elements in the storage area
*
* Returns : != (void *)0 is a pointer to the event control clock (OS_EVENT) associated with the
* created queue
* == (void *)0 if no event control blocks were available
*********************************************************************************************************
*/
//创建一个队列消息
OS_EVENT *OSQCreate (void **start, INT16U size) reentrant
{
OS_EVENT *pevent;
OS_Q *pq;
OS_ENTER_CRITICAL();
//进入临界区
pevent = OSEventFreeList; /* Get next free event control block */
//从空列表中摘下一个块
if (OSEventFreeList != (OS_EVENT *)0) { /* See if pool of free ECB pool was empty */
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
//检查ECB链表的OSEventFreeList是否为零,不为零赋值为零。
}
OS_EXIT_CRITICAL();
//退出临界区
if (pevent != (OS_EVENT *)0) { /* See if we have an event control block */
//判断事件控制块里面是否为空
OS_ENTER_CRITICAL(); /* Get a free queue control block */
//进入临界区
pq = OSQFreeList;
//从OSQFreeList指针指向pq
if (OSQFreeList != (OS_Q *)0) {
OSQFreeList = OSQFreeList->OSQPtr;
//检查OSQFreeList是否为零,不为零赋值为零。
}
OS_EXIT_CRITICAL();
//退出临界区
if (pq != (OS_Q *)0) { /* See if we were able to get a queue control block */
//判断pq是否有效
pq->OSQStart = start; /* Yes, initialize the queue */
pq->OSQEnd = &start[size];
pq->OSQIn = start;
pq->OSQOut = start;
pq->OSQSize = size;
pq->OSQEntries = 0;
pevent->OSEventType = OS_EVENT_TYPE_Q;
pevent->OSEventPtr = pq;
//对队列的内容进行填充
OSEventWaitListInit(pevent);
//把设置好的队列添加进等待列表
} else { /* No, since we couldn't get a queue control block */
OS_ENTER_CRITICAL(); /* Return event control block on error */
//进入临界区
pevent->OSEventPtr = (void *)OSEventFreeList;
//OSEventPtr指向一个空的块
OSEventFreeList = pevent;
//OSEventFreeList为空
OS_EXIT_CRITICAL();
//退出临界区
pevent = (OS_EVENT *)0;
}
}
return (pevent);
}
/*$PAGE*/
/*
*********************************************************************************************************
* FLUSH QUEUE
*
* Description : This function is used to flush the contents of the message queue.
*
* Arguments : none
*
* Returns : OS_NO_ERR upon success
* OS_ERR_EVENT_TYPE If you didn't pass a pointer to a queue
*********************************************************************************************************
*/
//允许用户删除一个消息队列中的所有消息,重新开始使用
INT8U OSQFlush (OS_EVENT *pevent) reentrant
{
OS_Q *pq;
OS_ENTER_CRITICAL();
//进入临界区
if (pevent->OSEventType != OS_EVENT_TYPE_Q) { /* Validate event block type */
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
//如果类型不是队列,而退出临界区并返回错误码
}
pq = pevent->OSEventPtr; /* Point to queue storage structure */
pq->OSQIn = pq->OSQStart;
pq->OSQOut = pq->OSQStart;
pq->OSQEntries = 0;
//把队列里面的东西进行初始化
OS_EXIT_CRITICAL();
//退出临界区
return (OS_NO_ERR);
}
/*$PAGE*/
/*
*********************************************************************************************************
* QUEUE MODULE INITIALIZATION
*
* Description : This function is called by uC/OS-II to initialize the message queue module. Your
* application MUST NOT call this function.
*
* Arguments : none
*
* Returns : none
*********************************************************************************************************
*/
//队列初始化
void OSQInit (void) reentrant
{
INT16U i;
for (i = 0; i < (OS_MAX_QS - 1); i++) { /* Init. list of free QUEUE control blocks */
OSQTbl[i].OSQPtr = &OSQTbl[i+1];
}
//初始化队列控制块
OSQTbl[OS_MAX_QS - 1].OSQPtr = (OS_Q *)0;
//队列的最后一个为零
OSQFreeList = &OSQTbl[0];
//队列的空块指向OSQTbl
}
/*$PAGE*/
/*
*********************************************************************************************************
* PEND ON A QUEUE FOR A MESSAGE
*
* Description: This function waits for a message to be sent to a queue
*
* Arguments : pevent is a pointer to the event control block associated with the desired queue
*
* timeout is an optional timeout period (in clock ticks). If non-zero, your task will
* wait for a message to arrive at the queue up to the amount of time
* specified by this argument. If you specify 0, however, your task will wait
* forever at the specified queue or, until a message arrives.
*
* err is a pointer to where an error message will be deposited. Possible error
* messages are:
*
* OS_NO_ERR The call was successful and your task received a message.
* OS_TIMEOUT A message was not received within the specified timeout
* OS_ERR_EVENT_TYPE You didn't pass a pointer to a queue
* OS_ERR_PEND_ISR If you called this function from an ISR and the result
* would lead to a suspension.
*
* Returns : != (void *)0 is a pointer to the message received
* == (void *)0 if no message was received or you didn't pass a pointer to a queue.
*********************************************************************************************************
*/
//队列等待
void *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) reentrant
{
void *msg;
OS_Q *pq;
OS_ENTER_CRITICAL();
//进入临界区
if (pevent->OSEventType != OS_EVENT_TYPE_Q) {/* Validate event block type */
OS_EXIT_CRITICAL();
*err = OS_ERR_EVENT_TYPE;
return ((void *)0);
//对事件的类型进行检查,如果不是队列,退出临界区并返回错误码
}
pq = pevent->OSEventPtr; /* Point at queue control block */
//指针指向一个队列块
if (pq->OSQEntries != 0) { /* See if any messages in the queue */
//如果队列中有消息
msg = *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();
*err = OS_NO_ERR;
//退出临界区并返回错误码
} else if (OSIntNesting > 0) { /* See if called from ISR ... */
OS_EXIT_CRITICAL(); /* ... can't PEND from an ISR */
*err = OS_ERR_PEND_ISR;
//如果存在中断发生,退出临界区并返回错误码
} else {
OSTCBCur->OSTCBStat |= OS_STAT_Q; /* Task will have to pend for a message to be posted */
//等到队列消息
OSTCBCur->OSTCBDly = timeout; /* Load timeout into TCB */
//设置超时时间
OSEventTaskWait(pevent); /* Suspend task until event or timeout occurs */
//把任务添加等待列表
OS_EXIT_CRITICAL();
//退出临界区
OSSched(); /* Find next highest priority task ready to run */
//进行一次调度
OS_ENTER_CRITICAL();
//进入临界区
if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) {/* Did we get a message? */
//判断是否收到了消息
OSTCBCur->OSTCBMsg = (void *)0; /* Extract message from TCB (Put there by QPost) */
//把OSTCBCur->OSTCBMsg 赋值为0
OSTCBCur->OSTCBStat = OS_STAT_RDY;
//TCB块的状态改为OS_STAT_RDY
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* No longer waiting for event */
//OSTCBEventPtr清空
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
//退出临界区并返回错误码
} else if (OSTCBCur->OSTCBStat & OS_STAT_Q) { /* Timed out if status indicates pending on Q */
//如果TCB的状态是OS_STAT_Q
OSEventTO(pevent);
//超时直接进入运行,不再等待
OS_EXIT_CRITICAL();
msg = (void *)0; /* No message received */
*err = OS_TIMEOUT; /* Indicate a timeout occured */
//退出临界区并返回错误码
} else {
msg = *pq->OSQOut++; /* Extract message from 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 Q */
pq->OSQOut = pq->OSQStart;
//如果队列头部等于尾部,重新把头尾移到头部
}
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
//事件的指针指0
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
//退出临界区并返回错误码
}
}
return (msg); /* Return message received (or NULL) */
//返回消息
}
/*$PAGE*/
/*
*********************************************************************************************************
* POST MESSAGE TO A QUEUE
*
* Description: This function sends a message to a queue
*
* Arguments : pevent is a pointer to the event control block associated with the desired queue
*
* msg is a pointer to the message to send. You MUST NOT send a NULL pointer.
*
* Returns : OS_NO_ERR The call was successful and the message was sent
* OS_Q_FULL If the queue cannot accept any more messages because it is full.
* OS_ERR_EVENT_TYPE If you didn't pass a pointer to a queue.
*********************************************************************************************************
*/
//发送一个队列消息
INT8U OSQPost (OS_EVENT *pevent, void *msg) reentrant
{
OS_Q *pq;
OS_ENTER_CRITICAL();
//进入临界区
if (pevent->OSEventType != OS_EVENT_TYPE_Q) { /* Validate event block type */
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
//如果不是队列,退出临界区并返回错误码
}
if (pevent->OSEventGrp) { /* See if any task pending on queue */
//判断是否有等待的任务
OSEventTaskRdy(pevent, msg, OS_STAT_Q); /* Ready highest priority task waiting on event */
//找出优先级最高的等待任务
OS_EXIT_CRITICAL();
//退出临界区
OSSched(); /* Find highest priority task ready to run */
return (OS_NO_ERR);
//退出临界区并返回错误码
} else {
pq = pevent->OSEventPtr; /* Point to queue control block */
//指针指向队列的块
if (pq->OSQEntries >= pq->OSQSize) { /* Make sure queue is not full */
//如果队列是否为空
OS_EXIT_CRITICAL();
return (OS_Q_FULL);
//退出临界区并返回错误码
} else {
*pq->OSQIn++ = msg; /* 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;
//如果到队尾了重新移到队头
}
OS_EXIT_CRITICAL();
//退出临界区
}
return (OS_NO_ERR);
}
}
/*$PAGE*/
/*
*********************************************************************************************************
* POST MESSAGE TO THE FRONT OF A QUEUE
*
* Description: This function sends a message to a queue but unlike OSQPost(), the message is posted at
* the front instead of the end of the queue. Using OSQPostFront() allows you to send
* 'priority' messages.
*
* Arguments : pevent is a pointer to the event control block associated with the desired queue
*
* msg is a pointer to the message to send. You MUST NOT send a NULL pointer.
*
* Returns : OS_NO_ERR The call was successful and the message was sent
* OS_Q_FULL If the queue cannot accept any more messages because it is full.
* OS_ERR_EVENT_TYPE If you didn't pass a pointer to a queue.
*********************************************************************************************************
*/
//将发送的消息插到消息队列的最前端
INT8U OSQPostFront (OS_EVENT *pevent, void *msg) reentrant
{
OS_Q *pq;
OS_ENTER_CRITICAL();
//进入临界区
if (pevent->OSEventType != OS_EVENT_TYPE_Q) { /* Validate event block type */
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
//如果不是队列,退出临界区并返回错误码
}
if (pevent->OSEventGrp) { /* See if any task pending on queue */
//判断是否有任务在等待
OSEventTaskRdy(pevent, msg, OS_STAT_Q); /* Ready highest priority task waiting on event */
//找出优先级最高的任务
OS_EXIT_CRITICAL();
//退出临界区
OSSched(); /* Find highest priority task ready to run */
//进行调度
return (OS_NO_ERR);
//返回错误码
} else {
pq = pevent->OSEventPtr; /* Point to queue control block */
//队列指针指向队列控制块
if (pq->OSQEntries >= pq->OSQSize) { /* Make sure queue is not full */
//如果队列为0
OS_EXIT_CRITICAL();
return (OS_Q_FULL);
//退出临界区并返回错误码
} else {
if (pq->OSQOut == pq->OSQStart) { /* Wrap OUT ptr if we are at the 1st queue entry */
pq->OSQOut = pq->OSQEnd;
//如果到队列的尾部了,重新移动到头部
}
pq->OSQOut--;
//?????
*pq->OSQOut = msg; /* Insert message into queue */
//在队列中插入消息
pq->OSQEntries++; /* Update the nbr of entries in the queue */
//数目依次加加
OS_EXIT_CRITICAL();
//退出临界区
}
return (OS_NO_ERR);
}
}
/*$PAGE*/
/*
*********************************************************************************************************
* QUERY A MESSAGE QUEUE
*
* Description: This function obtains information about a message queue.
*
* Arguments : pevent is a pointer to the event control block associated with the desired mailbox
*
* pdata is a pointer to a structure that will contain information about the message
* queue.
*
* Returns : OS_NO_ERR The call was successful and the message was sent
* OS_ERR_EVENT_TYPE If you are attempting to obtain data from a non queue.
*********************************************************************************************************
*/
INT8U OSQQuery (OS_EVENT *pevent, OS_Q_DATA *ppdata) reentrant
{
OS_Q *pq;
INT8U i;
INT8U *psrc;
INT8U *pdest;
OS_ENTER_CRITICAL();
//进入临界区
if (pevent->OSEventType != OS_EVENT_TYPE_Q) { /* Validate event block type */
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
//如果不是队列,退出临界区并返回错误码
}
ppdata->OSEventGrp = pevent->OSEventGrp; /* Copy message mailbox wait list */
psrc = &pevent->OSEventTbl[0];
pdest = &ppdata->OSEventTbl[0];
//把消息直接复制到pevent
for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
*pdest++ = *psrc++;
}
pq = (OS_Q *)pevent->OSEventPtr;
//OSEventPtr指向pq
if (pq->OSQEntries > 0) {
ppdata->OSMsg = pq->OSQOut; /* Get next message to return if available */
//查询下一个消息
} else {
ppdata->OSMsg = (void *)0;
//指针指向空
}
ppdata->OSNMsgs = pq->OSQEntries;
ppdata->OSQSize = pq->OSQSize;
//ppdata读取数目和尺寸
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
//退出临界区并返回错误码
}
#endif