1、下面是创建了一个长度为 5 的队列：

2、此刻任务 A 写一个数据 10 到 Queue：

3、任务 A 在写一个数据 20 到 Queue；

4、此刻任务 B 从 Queue 中读取，先读取第一个数据 10；

5、此刻 10 已经被读走，20 成为下一个即将被读取的数据：

# 1、Basic Feature

Queue 支持多个读者，所以，如果多个任务都在读一个已经空了的 Queue，那么他们都将进入阻塞状态，

## 1.3、Blocking on Queue Writes

Queue 支持多个写者，所以，如果多个任务都在写一个已经满了的 Queue，那么他们都将进入阻塞状态，出现这种情况的时候，一旦 Queue 有数据被读走，阻塞在写上的几个任务只有一个最高优先级的任务可以解除阻塞，进入 Ready；如果这几个任务的优先级一样，那么等待数据并处于阻塞的时间最长的那个任务将被解除阻塞；

# 2、Usage And APIs

## 2.1、xQueueCreate()

xQueueCreate() 接口用于创建一个 Queue，函数的原型为：

QueueHandle_t xQueueCreate (UBaseType_t uxQueueLength, UBaseType_t uxItemSize);

uxQueueLength：Queue 中能够存储的最大的 Items 的个数；

uxItemSize：一个 Item 的大小，单位为 Bytes；

Return：如果返回 NULL，说明创建 Queue 失败；否则将返回这个 Queue 的 Handle 句柄，后面操作这个 Queue 就靠这个句柄；

## 2.2、xQueueSendToBack()/xQueueSend And xQueueSendToFront()

BaseType_t xQueueSendToFront( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );
BaseType_t xQueueSendToBack( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );

xQueue：队列创建时候的句柄，用于表示向哪个队列写入数据；

pvItemToQueue：放入队列的 Item 的指针；

xTicksToWait：如果队列为满，则阻塞的最大时间；当被设置为 0 的时候，不阻塞，如果队列为满，则直接返回；如果设置为 portMAX_DELAY 的话，意味着如果队列为满，则会挂起这个任务；

Return：当成功放入队列，返回 pdTRUE；否则返回 errQUEUE_FULL；

BaseType_t xQueueReceive( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait );

xQueue：队列创建时候的句柄，用于表示向哪个队列写入数据；

pvBuffer：从队列读出来的数据指针；

xTicksToWait：如果队列为空，则为阻塞的最大时间；当被设置为 0 的时候，不阻塞，如果队列为空，则直接返回；如果设置为 portMAX_DELAY 的话，意味着如果队列为空，则会挂起这个任务；

Return：当成功读出队列的数据，返回 pdTRUE；否则返回 errQUEUE_EMPTY；

## 2.4、uxQueueMessagesWaiting()

UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue );

xQueue：队列创建时候的句柄，用于表示查看哪个队列还有几个 Item；

Return：当前队列中还有几个 Item 元素，如果返回 0，说明队列为空；

## 2.5、Example：Blocking when receiving from a queue

1、创建一个 Queue；

2、多个任务往 Queue 写数据，一个任务读数据；

static void vSenderTask( void *pvParameters )
{
int32_t lValueToSend;
BaseType_t xStatus;
/* Two instances of this task are created so the value that is sent to the
queue is passed in via the task parameter - this way each instance can use
a different value. The queue was created to hold values of type int32_t,
so cast the parameter to the required type. */
lValueToSend = ( int32_t ) pvParameters;
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; )
{
/* Send the value to the queue.
The first parameter is the queue to which data is being sent. The
queue was created before the scheduler was started, so before this task
started to execute.
The second parameter is the address of the data to be sent, in this case
The third parameter is the Block time – the time the task should be kept
in the Blocked state to wait for space to become available on the queue
should the queue already be full. In this case a block time is not
specified because the queue should never contain more than one item, and
therefore never be full. */
xStatus = xQueueSendToBack( xQueue, &lValueToSend, 0 );
if( xStatus != pdPASS )
{
/* The send operation could not complete because the queue was full -
this must be an error as the queue should never contain more than
one item! */
vPrintString( "Could not send to the queue.\r\n" );
}

}
}

4、创建 1 个接收数据的任务

static void vReceiverTask( void *pvParameters )
{
/* Declare the variable that will hold the values received from the queue. */
BaseType_t xStatus;
const TickType_t xTicksToWait = pdMS_TO_TICKS( 100 );
/* This task is also defined within an infinite loop. */
for( ;; )
{
/* This call should always find the queue empty because this task will
immediately remove any data that is written to the queue. */
if( uxQueueMessagesWaiting( xQueue ) != 0 )
{
vPrintString( "Queue should have been empty!\r\n" );
}
/* Receive data from the queue.
The first parameter is the queue from which data is to be received. The
queue is created before the scheduler is started, and therefore before this
task runs for the first time.
The second parameter is the buffer into which the received data will be
placed. In this case the buffer is simply the address of a variable that
has the required size to hold the received data.
The last parameter is the block time – the maximum amount of time that the
task will remain in the Blocked state to wait for data to be available
should the queue already be empty. */
if( xStatus == pdPASS )
{
/* Data was successfully received from the queue, print out the received
value. */
}
else
{
/* Data was not received from the queue even after waiting for 100ms.
This must be an error as the sending tasks are free running and will be
continuously writing to the queue. */
vPrintString( "Could not receive from the queue.\r\n" );
}
}
}

5、初始化如下：

/* Declare a variable of type QueueHandle_t. This is used to store the handle
to the queue that is accessed by all three tasks. */
QueueHandle_t xQueue;
int main( void )
{
/* The queue is created to hold a maximum of 5 values, each of which is
large enough to hold a variable of type int32_t. */
xQueue = xQueueCreate( 5, sizeof( int32_t ) );
if( xQueue != NULL )
{
/* Create two instances of the task that will send to the queue. The task
parameter is used to pass the value that the task will write to the queue,
so one task will continuously write 100 to the queue while the other task
will continuously write 200 to the queue. Both tasks are created at
priority 1. */
xTaskCreate( vSenderTask, "Sender1", 1000, ( void * ) 100, 1, NULL );
xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL );
priority 2, so above the priority of the sender tasks. */
/* Start the scheduler so the created tasks start executing. */
}
else
{
/* The queue could not be created. */
}

/* If all is well then main() will never reach here as the scheduler will
now be running the tasks. If main() does reach here then it is likely that
there was insufficient FreeRTOS heap memory available for the idle task to be
for( ;; );
}

## 2.6、Receiving Data From Multiple Sources

typedef struct {
uint8_t ucValue;
DataSource_t eDataSourceID;
} Data_t;

typedef enum
{
eCanBusSender,
eHMISender
} DataSource_t;

Example：

/* Define an enumerated type used to identify the source of the data. */
typedef enum
{
eSender1,
eSender2
} DataSource_t;
/* Define the structure type that will be passed on the queue. */
typedef struct
{
uint8_t ucValue;
DataSource_t eDataSource;
} Data_t;
/* Declare two variables of type Data_t that will be passed on the queue. */
static const Data_t xStructsToSend[ 2 ] =
{
{ 100, eSender1 }, /* Used by Sender1. */
{ 200, eSender2 } /* Used by Sender2. */
};

static void vSenderTask( void *pvParameters )
{
BaseType_t xStatus;
const TickType_t xTicksToWait = pdMS_TO_TICKS( 100 );
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; )
{
/* Send to the queue.
The second parameter is the address of the structure being sent. The
address is passed in as the task parameter so pvParameters is used
directly.
The third parameter is the Block time - the time the task should be kept
in the Blocked state to wait for space to become available on the queue
if the queue is already full. A block time is specified because the
sending tasks have a higher priority than the receiving task so the queue
is expected to become full. The receiving task will remove items from
the queue when both sending tasks are in the Blocked state. */
xStatus = xQueueSendToBack( xQueue, pvParameters, xTicksToWait );
if( xStatus != pdPASS )
{
/* The send operation could not complete, even after waiting for 100ms.
This must be an error as the receiving task should make space in the
queue as soon as both sending tasks are in the Blocked state. */
vPrintString( "Could not send to the queue.\r\n" );
}
}
}

static void vReceiverTask( void *pvParameters )
{
/* Declare the structure that will hold the values received from the queue. */
BaseType_t xStatus;
/* This task is also defined within an infinite loop. */
for( ;; )
{
/* Because it has the lowest priority this task will only run when the
sending tasks are in the Blocked state. The sending tasks will only enter
the Blocked state when the queue is full so this task always expects the
number of items in the queue to be equal to the queue length, which is 3 in
this case. */
if( uxQueueMessagesWaiting( xQueue ) != 3 )
{
vPrintString( "Queue should have been full!\r\n" );
}
The second parameter is the buffer into which the received data will be
placed. In this case the buffer is simply the address of a variable that
has the required size to hold the received structure.
The last parameter is the block time - the maximum amount of time that the
task will remain in the Blocked state to wait for data to be available
if the queue is already empty. In this case a block time is not necessary
because this task will only run when the queue is full. */
if( xStatus == pdPASS )
{
/* Data was successfully received from the queue, print out the received
value and the source of the value. */
{
vPrintStringAndNumber( "From Sender 1 = ", xReceivedStructure.ucValue );
}
else
{
vPrintStringAndNumber( "From Sender 2 = ", xReceivedStructure.ucValue );
}
}
else
{
/* Nothing was received from the queue. This must be an error as this
task should only run when the queue is full. */
vPrintString( "Could not receive from the queue.\r\n" );
}
}
}

int main( void )
{
/* The queue is created to hold a maximum of 3 structures of type Data_t. */
xQueue = xQueueCreate( 3, sizeof( Data_t ) );
if( xQueue != NULL )
{
/* Create two instances of the task that will write to the queue. The
parameter is used to pass the structure that the task will write to the
queue, so one task will continuously send xStructsToSend[ 0 ] to the queue
while the other task will continuously send xStructsToSend[ 1 ]. Both
tasks are created at priority 2, which is above the priority of the receiver. */
xTaskCreate( vSenderTask, "Sender1", 1000, &( xStructsToSend[ 0 ] ), 2, NULL );
xTaskCreate( vSenderTask, "Sender2", 1000, &( xStructsToSend[ 1 ] ), 2, NULL );
priority 1, so below the priority of the sender tasks. */
/* Start the scheduler so the created tasks start executing. */
}
else
{
/* The queue could not be created. */
}

/* If all is well then main() will never reach here as the scheduler will
now be running the tasks. If main() does reach here then it is likely that
there was insufficient heap memory available for the idle task to be created.
for( ;; );
}

t2 时刻，Sender 2 希望往 Queue 放数据，结果已经满了，直接进入 Blocked；

t4 时刻，由于 Sender 1 和 Sender 2 都阻塞在写 Queue，此刻 Queue 不为满，所以 Sender 1 得以执行，并往 Queue 中写入一个 Item；

## 2.7、Working with Large or Variable Sized Data

1、当通过指针在任务之间共享内存时，必须确保，任务不会同时修改内存内容或执行任何其他操作，否则可能导致内存内容无效或不一致。正常情况下，在读共享内存的任务读走有效数据之前，只有发送者才允许取访问该共享内存；

2、如果指针指向的内存是动态分配的，或者从已经准备好的内存池中获取的，那么需要有一个任务去释放他们；被释放后，不允许在访问；

Example：

/* Declare a variable of type QueueHandle_t to hold the handle of the queue being created. */
QueueHandle_t xPointerQueue;
/* Create a queue that can hold a maximum of 5 pointers, in this case character pointers. */
xPointerQueue = xQueueCreate( 5, sizeof( char * ) );

Sender 分配内存，发送字符串指针到 Queue；

/* A task that obtains a buffer, writes a string to the buffer, then sends the address of the
buffer to the queue created in Listing 52. */
{
char *pcStringToSend;
const size_t xMaxStringLength = 50;
BaseType_t xStringNumber = 0;
for( ;; )
{
/* Obtain a buffer that is at least xMaxStringLength characters big. The implementation
of prvGetBuffer() is not shown – it might obtain the buffer from a pool of pre-allocated
buffers, or just allocate the buffer dynamically. */
pcStringToSend = ( char * ) prvGetBuffer( xMaxStringLength );
/* Write a string into the buffer. */
snprintf( pcStringToSend, xMaxStringLength, "String number %d\r\n", xStringNumber );
/* Increment the counter so the string is different on each iteration of this task. */
xStringNumber++;
/* Send the address of the buffer to the queue that was created in Listing 52. The
address of the buffer is stored in the pcStringToSend variable.*/
xQueueSend( xPointerQueue, /* The handle of the queue. */
&pcStringToSend, /* The address of the pointer that points to the buffer. */
portMAX_DELAY );
}
}

void vStringReceivingTask( void *pvParameters )
{
for( ;; )
{
xQueueReceive( xPointerQueue, /* The handle of the queue. */
portMAX_DELAY );
/* The buffer holds a string, print it out. */
/* The buffer is not required any more - release it so it can be freed, or re-used. */
}
}

### 2.7.1、Send Different Types and Lengths of Data

1、首先定义一些基本的结构，数据的 Source（eIPEvent_t）以及发送给 Queue 的 Item：

/* A subset of the enumerated types used in the TCP/IP stack to identify events. */
typedef enum
{
eNetworkDownEvent = 0, /* The network interface has been lost, or needs (re)connecting. */
eNetworkRxEvent, /* A packet has been received from the network. */
eTCPAcceptEvent, /* FreeRTOS_accept() called to accept or wait for a new client. */
/* Other event types appear here but are not shown in this listing. */
} eIPEvent_t;
/* The structure that describes events, and is sent on a queue to the TCP/IP task. */
{
/* An enumerated type that identifies the event. See the eIPEvent_t definition above. */
eIPEvent_t eEventType;
/* A generic pointer that can hold a value, or point to a buffer. */
void *pvData;
} IPStackEvent_t;

2、网络接收的数据通过 Queue 传递给 TCP/IP 协议栈，eEventType 标记为 eNetworkRxEvent：

void vSendRxDataToTheTCPTask( NetworkBufferDescriptor_t *pxRxedData ) {
IPStackEvent_t xEventStruct;

/* Complete the IPStackEvent_t structure. The received data is stored in
pxRxedData. */
xEventStruct.eEventType = eNetworkRxEvent;
xEventStruct.pvData = ( void * ) pxRxedData;
/* Send the IPStackEvent_t structure to the TCP/IP task. */
}

3、Accept 事件也会给 TCP/IP 任务发送数据：

void vSendAcceptRequestToTheTCPTask( Socket_t xSocket ) {
IPStackEvent_t xEventStruct;

/* Complete the IPStackEvent_t structure. */
xEventStruct.eEventType = eTCPAcceptEvent;
xEventStruct.pvData = ( void * ) xSocket;
/* Send the IPStackEvent_t structure to the TCP/IP task. */
}

4、网络 Down 掉的事件也会发送给 TCP/IP 任务处理：

void vSendNetworkDownEventToTheTCPTask( Socket_t xSocket ) {
IPStackEvent_t xEventStruct;

/* Complete the IPStackEvent_t structure. */
xEventStruct.eEventType = eNetworkDownEvent;
xEventStruct.pvData = NULL; /* Not used, but set to NULL for completeness. */
/* Send the IPStackEvent_t structure to the TCP/IP task. */
}

5、在 TCP/IP 协议栈受到这些消息后，根据消息的来源，获取数据（均以指针传递），并处理：

IPStackEvent_t xReceivedEvent;
/* Block on the network event queue until either an event is received, or xNextIPSleep ticks
pass without an event being received. eEventType is set to eNoEvent in case the call to
xQueueReceive() returns because it timed out, rather than because an event was received. */
/* Which event was received, if any? */
{
case eNetworkDownEvent :
/* Attempt to (re)establish a connection. This event is not associated with any
data. */
prvProcessNetworkDownEvent();
break;
case eNetworkRxEvent:
/* The network interface has received a new packet. A pointer to the received data
is stored in the pvData member of the received IPStackEvent_t structure. Process
prvHandleEthernetPacket( ( NetworkBufferDescriptor_t * )( xReceivedEvent.pvData ) );
break;
case eTCPAcceptEvent:
/* The FreeRTOS_accept() API function was called. The handle of the socket that is
accepting a connection is stored in the pvData member of the received IPStackEvent_t
structure. */
xSocket = ( FreeRTOS_Socket_t * ) ( xReceivedEvent.pvData );
xTCPCheckNewClient( pxSocket );
break;
/* Other event types are processed in the same way, but are not shown here. */
}

## 2.8、Receiving From Multiple Queues

Queue Set 允许用户定义对个 Queue，并且调用 FreeRTOS 的 xQueueCreateSet 定义一个队列集合，将多个 Queue 通过 xQueueAddToSet 的方式添加到 Queue Set；每个任务只操作自己的 Queue，在接收端使用 xQueueSelectFromSet 配合 xQueueReceive 来接收数据；

Example：

/* Declare two variables of type QueueHandle_t. Both queues are added to the same
queue set. */
static QueueHandle_t xQueue1 = NULL, xQueue2 = NULL;
/* Declare a variable of type QueueSetHandle_t. This is the queue set to which the
static QueueSetHandle_t xQueueSet = NULL;
int main( void )
{
/* Create the two queues, both of which send character pointers. The priority
of the receiving task is above the priority of the sending tasks, so the queues
will never have more than one item in them at any one time*/
xQueue1 = xQueueCreate( 1, sizeof( char * ) );
xQueue2 = xQueueCreate( 1, sizeof( char * ) );
/* Create the queue set. Two queues will be added to the set, each of which can
contain 1 item, so the maximum number of queue handles the queue set will ever
have to hold at one time is 2 (2 queues multiplied by 1 item per queue). */
xQueueSet = xQueueCreateSet( 1 * 2 );
/* Add the two queues to the set. */
/* Create the tasks that send to the queues. */
/* Create the task that reads from the queue set to determine which of the two
queues contain data. */
/* Start the scheduler so the created tasks start executing. */
/* As normal, vTaskStartScheduler() should not return, so the following lines
Will never execute. */
for( ;; );
return 0;
}

void vSenderTask1( void *pvParameters )
{
const TickType_t xBlockTime = pdMS_TO_TICKS( 100 );
const char * const pcMessage = "Message from vSenderTask1\r\n";
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; )
{
/* Block for 100ms. */
/* Send this task's string to xQueue1. It is not necessary to use a block
time, even though the queue can only hold one item. This is because the
priority of the task that reads from the queue is higher than the priority of
this task; as soon as this task writes to the queue it will be pre-empted by
by the time the call to xQueueSend() returns. The block time is set to 0. */
xQueueSend( xQueue1, &pcMessage, 0 );
}
}
/*-----------------------------------------------------------*/
{
const TickType_t xBlockTime = pdMS_TO_TICKS( 200 );
const char * const pcMessage = "Message from vSenderTask2\r\n";
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; )
{
/* Block for 200ms. */
/* Send this task's string to xQueue2. It is not necessary to use a block
time, even though the queue can only hold one item. This is because the
priority of the task that reads from the queue is higher than the priority of
this task; as soon as this task writes to the queue it will be pre-empted by
by the time the call to xQueueSend() returns. The block time is set to 0. */
xQueueSend( xQueue2, &pcMessage, 0 );
}
}

void vReceiverTask( void *pvParameters )
{
QueueHandle_t xQueueThatContainsData;
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; )
{
/* Block on the queue set to wait for one of the queues in the set to contain data.
Cast the QueueSetMemberHandle_t value returned from xQueueSelectFromSet() to a
QueueHandle_t, as it is known all the members of the set are queues (the queue set
does not contain any semaphores). */
xQueueThatContainsData = ( QueueHandle_t ) xQueueSelectFromSet( xQueueSet,
portMAX_DELAY );
/* An indefinite block time was used when reading from the queue set, so
xQueueSelectFromSet() will not have returned unless one of the queues in the set
contained data, and xQueueThatContainsData cannot be NULL. Read from the queue. It
is not necessary to specify a block time because it is known the queue contains
data. The block time is set to 0. */
/* Print the string received from the queue. */
}
}

## 2.9、Queue to Create a Mailbox

Queue 也可以用作 MailBox 的实现；在 FreeRTOS 中，mailbox 的实现是靠一个长度的 Queue 来做的；

Mailbox 用来为保持数据，以供其他的任务或者 ISR 来读取；在 mailbox 中的数据不会被冲掉，只能被重写；

BaseType_t xQueueOverwrite( QueueHandle_t xQueue, const void * pvItemToQueue );

xQueue：Queue 的句柄；

pvItemToQueue：要更新的数据指针；

Return：返回 pdPASS；

BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait );

3 个参数：

xQueue：Queue 的句柄；

pvBuffer：获取到的数据指针；

xTicksToWait：如果 mailbox 没有数据，阻塞的时间；

typedef struct xExampleStructure
{
TickType_t xTimeStamp;
uint32_t ulValue;
} Example_t;
/* A mailbox is a queue, so its handle is stored in a variable of type
QueueHandle_t. */
QueueHandle_t xMailbox;

void vAFunction( void )
{
/* Create the queue that is going to be used as a mailbox. The queue has a
length of 1 to allow it to be used with the xQueueOverwrite() API function, which
is described below. */
xMailbox = xQueueCreate( 1, sizeof( Example_t ) );
}

void vUpdateMailbox( uint32_t ulNewValue )
{
/* Example_t was defined in Listing 67. */
Example_t xData;
/* Write the new data into the Example_t structure.*/
xData.ulValue = ulNewValue;
/* Use the RTOS tick count as the time stamp stored in the Example_t structure. */
/* Send the structure to the mailbox - overwriting any data that is already in the
mailbox. */
xQueueOverwrite( xMailbox, &xData );
}

BaseType_t vReadMailbox( Example_t *pxData )
{
TickType_t xPreviousTimeStamp;
BaseType_t xDataUpdated;

/* This function updates an Example_t structure with the latest value received
from the mailbox. Record the time stamp already contained in *pxData before it
gets overwritten by the new data. */
xPreviousTimeStamp = pxData->xTimeStamp;
/* Update the Example_t structure pointed to by pxData with the data contained in
the mailbox. If xQueueReceive() was used here then the mailbox would be left
empty, and the data could not then be read by any other tasks. Using
A block time is specified, so the calling task will be placed in the Blocked
state to wait for the mailbox to contain data should the mailbox be empty. An
infinite block time is used, so it is not necessary to check the value returned
from xQueuePeek(), as xQueuePeek() will only return when data is available. */
xQueuePeek( xMailbox, pxData, portMAX_DELAY );
/* Return pdTRUE if the value read from the mailbox has been updated since this
function was last called. Otherwise return pdFALSE. */
if( pxData->xTimeStamp > xPreviousTimeStamp )
{
xDataUpdated = pdTRUE;
}
else
{
xDataUpdated = pdFALSE;
}

return xDataUpdated;
}

posted on 2020-10-09 14:33  信义勤爱  阅读(403)  评论(0编辑  收藏  举报