FreeRTOS学习笔记 & FreeRTOS消息队列

FreeRTOS消息队列

消息队列简介

队列是为了任务与任务、任务与中断之间的通信而准备的,可以在任务与任务、任务与中断之间传递消息,队列中可以存储有限的、大小固定的数据项目。任务与任务、任务与中断之间要交流的数据保存在队列中,叫做队列项目。队列所能保存的最大数据项目数量叫做队列的长度,创建队列的时候会指定数据项目的大小和队列的长度。由于队列用来传递消息的,所以也称为消息队列

消息队列数据存储

通常队列采用先进先出(FIFO)的存储缓冲机制,也就是往队列发送数据的时候(也叫入队)永远都是发送到队列的尾部,而从队列提取数据的时候(也叫出队)是从队列的头部提取的。但是也可以使用 LIFO 的存储缓冲,也就是后进先出,FreeRTOS 中的队列也提供了 LIFO 的存储缓冲机制。
数据发送到队列中会导致数据拷贝,也就是将要发送的数据拷贝到队列中,这就意味着在队列中存储的是数据的原始值,而不是原数据的引用(即只传递数据的指针),这个也叫做值传递。
采用值传递的话虽然会导致数据拷贝,会浪费一点时间,但是一旦将消息发送到队列中原始的数据缓冲区就可以删除掉或者覆写,这样的话这些缓冲区就可以被重复的使用。FreeRTOS 中使用队列传递消息的话虽然使用的是数据拷贝,但是也可以使用引用来传递消息,直接往队列中发送指向这个消息的地址指针就可以了!这样当要发送的消息数据太大的时候就可以直接发送消息缓冲区的地址指针

消息队列阻塞机制

出队阻塞

当任务尝试从一个队列中读取消息的时候可以指定一个阻塞时间,这个阻塞时间就是当任务从队列中读取消息无效的时候任务阻塞的时间。
出队就是从队列中读取消息,出队阻塞是针对从队列中读取消息的任务而言的。

比如任务 A 用于处理串口接收到的数据,串口接收到数据以后就会放到队列 Q 中,任务 B 从队列 Q 中读取数据。但是如果此时队列 Q 是空的,说明还没有数据,任务 B 这时候来读取的话肯定是获取不到任何东西,那该怎么办呢?
任务 B 现在有三种选择,
一:二话不说扭头就走,
二:要不我在等等吧,等一会看看,说不定一会就有数据了,
三:死等,死也要等到你有数据!

选哪一个就是由这个阻塞时间决定的,这个阻塞时间单位是时钟节拍数。阻塞时间为 0 的话就是不阻塞,没有数据的话就马上返回任务继续执行接下来的代码,对应第一种选择。如果阻塞时间为 0~ portMAX_DELAY 之间,当任务没有从队列中获取到消息的话就进入阻塞态,阻塞时间指定了任务进入阻塞态的时间,当阻塞时间到了以后还没有接收到数据的话就退出阻塞态,返回任务接着运行下面的代码,如果在阻塞时间内接收到了数据就立即返回,执行任务中下面的代码,这种情况对应第二种选择。当阻
塞时间设置为 portMAX_DELAY 的话,任务就会一直进入阻塞态等待,直到接收到数据为止!这个就是第三种选择。

入队阻塞

入队说的是向队列中发送消息,将消息加入到队列中。和出队阻塞一样,当一个任务向队列发送消息的话也可以设置阻塞时间。比如任务 B 向消息队列 Q发送消息,但是此时队列 Q 是满的,那肯定是发送失败的。此时任务 B 就会遇到和上面任务 A 一样的问题,这两种情况的处理过程是类似的,只不过一个是向队列 Q 发送消息,一个是从队列 Q 读取消息而已

消息队列控制块

FreeRTOS 的消息队列控制块由多个元素组成,当消息队列被创建时,系统会为控制块分配对应的内存空间,用于保存消息队列的一些信息如消息的存储位置,头指针 pcHead、尾指针 pcTail、消息大小 uxItemSize 以及队列长度uxLength,以及当前队列消息个数 uxMessagesWaiting 等

typedef struct QueueDefinition
{
		int8_t *pcHead; (1)
		int8_t *pcTail; (2)
		int8_t *pcWriteTo; (3)
		union
		{
			int8_t *pcReadFrom; (4)
			UBaseType_t uxRecursiveCallCount; (5)
		} u;
		List_t xTasksWaitingToSend; (6)
		List_t xTasksWaitingToReceive; (7)
		volatile UBaseType_t uxMessagesWaiting; (8)
		UBaseType_t uxLength; (9)
		UBaseType_t uxItemSize; (10)
		volatile int8_t cRxLock; (11)
		volatile int8_t cTxLock; (12)
		#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) &&( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
			uint8_t ucStaticallyAllocated;
		#endif
		#if ( configUSE_QUEUE_SETS == 1 )
			struct QueueDefinition *pxQueueSetContainer;
		#endif
		#if ( configUSE_TRACE_FACILITY == 1 )
			UBaseType_t uxQueueNumber;
			uint8_t ucQueueType;
		#endif
} xQUEUE;

typedef xQUEUE Queue_t;

代码(1):pcHead 指向队列消息存储区起始位置,即第一个消息空间。
代码(2):pcTail 指向队列消息存储区结束位置地址。
代码(3):pcWriteTo 指向队列消息存储区下一个可用消息空间。
代码(4):pcReadFrom 与 uxRecursiveCallCount 是一对互斥变量,使用联合体用来确保两个互斥的结构体成员不会同时出现。当结构体用于队列时,pcReadFrom 指向出队消息空间的最后一个,见文知义,就是读取消息时候是从pcReadFrom 指向的空间读取消息内容。
代码(5):当结构体用于互斥量时,uxRecursiveCallCount 用于计数,记录递归互斥量被“调用”的次数。
代码(6):xTasksWaitingToSend 是一个发送消息阻塞列表,用于保存阻塞在此队列的任务,任务按照优先级进行排序,由于队列已满,想要发送消息的任务无法发送消息。
代码(7):xTasksWaitingToReceive 是一个获取消息阻塞列表,用于保存阻塞在此队列的任务,任务按照优先级进行排序,由于队列是空的,想要获取消息的任务无法获取到消息。
代码(8):uxMessagesWaiting 用于记录当前消息队列的消息个数,如果消息队列被用于信号量的时候,这个值就表示有效信号量个数。
代码(9):uxLength 表示队列的长度,也就是能存放多少消息。
代码(10):uxItemSize 表示单个消息的大小。
代码(11):队列上锁后,储存从队列收到的列表项数目,也就是出队的数量,如果队列没有上锁,设置为 queueUNLOCKED。
代码(12):队列上锁后,储存发送到队列的列表项数目,也就是入队的数量,如果队列没有上锁,设置为 queueUNLOCKED。
这两个成员变量为 queueUNLOCKED 时,表示队列未上锁;
当这两个成员变量为 queueLOCKED_UNMODIFIED 时,表示队列上锁。

消息队列操作示图

在这里插入图片描述
图中任务 A 要向任务 B 发送消息,这个消息是 x 变量的值。首先创建一个队列,并且指定队列的长度和每条消息的长度。这里我们创建了一个长度为 4的队列,因为要传递的是 x 值,而 x 是个 int 类型的变量,所以每条消息的长度就是 int 类型的长度,在 STM32 中是 4 字节,即每条消息是 4 个字节的

xQueueCreate( (UBaseType_t) 4, sizeof(unsigned int) );

在这里插入图片描述
图中任务 A 的变量 x 值为 10,将这个值发送到消息队列中。此时队列剩余长度就是 3 了。
前面说了向队列中发送消息是采用拷贝的方式,所以一旦消息发送完成变量 x 就可以再次被使用,赋其他的值

int x = 10;
xQueueSend( xQueue, ( void * )&x, portMAX_Delay );

在这里插入图片描述
图中任务 A 又向队列发送了一个消息,即新的 x 的值,这里是 20。此时队列剩余长度为 2

int x = 20;
xQueueSend( xQueue, ( void * )&x, portMAX_Delay );

在这里插入图片描述
图中任务 B 从队列中读取消息,并将读取到的消息值赋值给 y,这样 y 就等于 10 了。
任务 B 从队列中读取消息完成以后可以选择清除掉这个消息或者不清除。当选择清除这个消息的话其他任务或中断就不能获取这个消息了,而且队列剩余大小就会加一,变成 3。如果不清除的话其他任务或中断也可以获取这个消息,而队列剩余大小依旧是 2。

int * x ;
int y;
xQueueReceive( xQueue,( void * )&x,portMAX_Delay );
y = *x;

常用消息队列 API 函数

xQueueCreate 创建一个新队列

configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中被设置为 1,才能使用此 RTOS API 函数

#define configSUPPORT_DYNAMIC_ALLOCATION  1

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
                             UBaseType_t uxItemSize );
参数:
uxQueueLength  	队列可同时容纳的最大项目数 。
uxItemSize  	存储队列中的每个数据项所需的大小(以字节为单位)。
数据项按副本排队,而不是按引用排队, 因此该值为每个排队项目将被复制的字节数。队列中每个数据项必须大小相同。

Returns:
如果队列创建成功,则返回所创建队列的句柄 。 如果创建队列所需的内存无法 分配 ,则返回 NULL。
用法示例:

struct AMessage
{
    char ucMessageID;
    char ucData[ 20 ];
};

void vATask( void *pvParameters )
{
	QueueHandle_t xQueue1, xQueue2;

    /* Create a queue capable of containing 10 unsigned long values. */
    xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );

    if( xQueue1 == NULL )
    {
        /* Queue was not created and must not be used. */
    }

    /* Create a queue capable of containing 10 pointers to AMessage
    structures.  These are to be queued by pointers as they are
    relatively large structures. */
    xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );

    if( xQueue2 == NULL )
    {
        /* Queue was not created and must not be used. */
    }

    /* ... Rest of task code. */
 }

vQueueDelete 删除队列

void vQueueDelete( QueueHandle_t xQueue );

参数:
xQueue  	要删除的队列的句柄

xQueueSend 向队列中发送一个消息(从队列尾插入)(任务中使用)

它等同于 xQueueSendToBack()
使用此函数发送消息就是使用的FIFO(先入先出方式)

#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \
    xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )

参数:

xQueue 	队列的句柄,数据项将发布到此队列。
pvItemToQueue 	指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,
				因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
xTicksToWait 	如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。
				如果队列已满,并且 xTicksToWait 设置为0 ,调用将立即返回。时间在 tick 周期中定义,因此如果需要,
				应使用常量portTICK_PERIOD_MS 转换为实时。
如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

Returns:如果成功发布项目,返回 pdTRUE,否则返回 errQUEUE_FULL

用法示例:
struct AMessage
{
   char ucMessageID;
   char ucData[ 20 ];
} xMessage;

unsigned long ulVar = 10UL;

void vATask( void *pvParameters )
{
	 QueueHandle_t xQueue1, xQueue2;
	 struct AMessage *pxMessage;
	
	    /* Create a queue capable of containing 10 unsigned long values. */
	    xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
	
	    /* Create a queue capable of containing 10 pointers to AMessage structures.
	    These should be passed by pointer as they contain a lot of data. */
	    xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
	
	    if( xQueue1 != 0 )
	    {
	        /* Send an unsigned long.  Wait for 10 ticks for space to become
	        available if necessary. */
	        if( xQueueSend( xQueue1,
	                       ( void * ) &ulVar,
	                       ( TickType_t ) 10 ) != pdPASS )
	        {
	            /* Failed to post the message, even after 10 ticks. */
	        }
	    }
	
	    if( xQueue2 != 0 )
	    {
	        /* 发送一个指向struct AMessage对象的指针. */
	        pxMessage = & xMessage;
	        xQueueSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
	    }
	
		/* ... Rest of task code. */
}

xQueueSendFromISR向队列中发送一个消息(从队列尾插入)(中断中使用)

将消息发送到队列尾部,在中断服务程序中使用此函数是安全的

#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \
    xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )

参数:
xQueue 	队列的句柄,数据项将发布到此队列。
pvItemToQueue 	指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,
				因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
pxHigherPriorityTaskWoken 	如果发送到队列导致任务解除阻塞,且解除阻塞的任务的优先级高于当前运行的任务,
							则xQueueSendFromISR() 会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。 
							如果 xQueueSendFromISR() 将此值设置为 pdTRUE,则应在中断退出前请求上下文切换。
从 FreeRTOS V7.3.0 开始,pxHigherPriorityTaskWoken 是一个可选参数,可设置为 NULL。

Returns:
如果将数据成功发送至队列,则返回 pdTRUE,否则返回 errQUEUE_FULL

用法示例:
void vBufferISR( void )
{
	char cIn;
	BaseType_t xHigherPriorityTaskWoken;

    /* We have not woken a task at the start of the ISR. */
    xHigherPriorityTaskWoken = pdFALSE;

    /* Loop until the buffer is empty. */
    do
    {
        /* Obtain a byte from the buffer. */
        cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );

        /* Post the byte. */
        xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );

    } while( portINPUT_BYTE( BUFFER_COUNT ) );
    
	    /* Actual macro used here is port specific. */
	    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

xQueueSendToFront 向队列中发送一个消息(从队列头插入)(任务中使用)

使用此函数发送消息就是使用的LIFO(后入先出方式)

#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) \
    xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )

参数:
xQueue 	队列的句柄,数据项将发布到此队列。
pvItemToQueue 	指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,
				因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
xTicksToWait 	如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。
				如果设置为 0,调用将立即返回。时间以滴答周期为单位定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。
如果 INCLUDE_vTaskSuspend设置为 “1”,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

Returns:
如果成功发布项目,返回 pdTRUE,否则返回 errQUEUE_FULL

用法示例:
struct AMessage
{
    char ucMessageID;
    char ucData[ 20 ];
} xMessage;

unsigned long ulVar = 10UL;

void vATask( void *pvParameters )
{
	QueueHandle_t xQueue1, xQueue2;
	struct AMessage *pxMessage;

    /* Create a queue capable of containing 10 unsigned long values. */
    xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );

    /* Create a queue capable of containing 10 pointers to AMessage
    structures.  These should be passed by pointer as they contain a lot of
    data. */
    xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );

    /* ... */

    if( xQueue1 != 0 )
    {
        /* Send an unsigned long.  Wait for 10 ticks for space to become
        available if necessary. */
        if( xQueueSendToFront( xQueue1,
                              ( void * ) &ulVar,
                              ( TickType_t ) 10 ) != pdPASS )
        {
            /* Failed to post the message, even after 10 ticks. */
        }
    }

    if( xQueue2 != 0 )
    {
        /* Send a pointer to a struct AMessage object.  Don't block if the
        queue is already full. */
        pxMessage = & xMessage;
        xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
    }

	/* ... Rest of task code. */
}

xQueueSendToFrontFromISR 向队列中发送一个消息(从队列头插入)(中断中使用)

从队列头部入队一个数据项。可在中断服务程序中使用此函数
数据项通过复制而非引用入队,因此最好只发送较小的项,或者发送指向该项的指针

#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \
    xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )
    
参数:
xQueue 	队列的句柄,数据项将发布到此队列。
pvItemToQueue 	指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
pxHigherPriorityTaskWoken 	如果发送到队列导致某个任务解除阻塞,且被解除阻塞的任务的优先级高于当前运行的任务,则 xQueueSendToFrontFromISR() 会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。 如果 xQueueSendToFrontFromISR() 将此值设置为 pdTRUE,则应在中断退出前请求上下文切换。
从 FreeRTOS V7.3.0 开始,pxHigherPriorityTaskWoken 是一个可选参数,可设置为 NULL。

Returns:
若数据成功发送至队列,则返回 pdPass,否则返回 errQUEUE_FULL

用法示例:

void vBufferISR( void )
{
	char cIn;
	BaseType_t xHigherPriorityTaskWoken;

    /* We have not woken a task at the start of the ISR. */
    xHigherPriorityTaskWoken = pdFALSE;

    /* Obtain a byte from the buffer. */
    cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );

    if( cIn == EMERGENCY_MESSAGE )
    {
        /* Post the byte to the front of the queue. */
        xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
    }
    else
    {
        /* Post the byte to the back of the queue. */
        xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
    }

    /* Did sending to the queue unblock a higher priority task? */
    if( xHigherPriorityTaskWoken )
    {
        /* Actual macro used here is port specific. */
        portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
    }
}

xQueueSendToBack 向队列中发送一个消息(从队列尾插入)(任务中使用)

此函数与xQueueSend 功能一至

xQueueSendToBackFromISR 向队列中发送一个消息(从队列尾插入)(中断中使用)

此函数与xQueueSendFromISR 功能一至

#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) \
    xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
    
#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \
    xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )

xQueueReceive 从消息队列中读取消息 (任务中使用)

从队列中接收项目。该项目通过复制接收,因此必须提供足够大小的缓冲区

BaseType_t xQueueReceive(	QueueHandle_t xQueue,
                            void *pvBuffer,
                            TickType_t xTicksToWait
                        );
参数:
xQueue 	要从中接收项目的队列的句柄。
pvBuffer 	指向缓冲区的指针,接收到的项目将被复制到这个缓冲区。
xTicksToWait  	如果在调用时队列为空,则任务应阻塞等待项目接收的最长时间。 
				如果队列为空,将 xTicksToWait 设置为 0 将导致函数立即返回。时间在滴答周期中定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。
如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

Return:
如果从队列成功接收到项目,返回 pdTRUE,否则返回 pdFALSE

用法示例:
/* Define a variable of type struct AMMessage.  The examples below demonstrate
how to pass the whole variable through the queue, and as the structure is
moderately large, also how to pass a reference to the variable through a queue. */
struct AMessage
{
   char ucMessageID;
   char ucData[ 20 ];
} xMessage;

/* Queue used to send and receive complete struct AMessage structures. */
QueueHandle_t xStructQueue = NULL;

/* Queue used to send and receive pointers to struct AMessage structures. */
QueueHandle_t xPointerQueue = NULL;


void vCreateQueues( void )
{
   xMessage.ucMessageID = 0xab;
   memset( &( xMessage.ucData ), 0x12, 20 );

   /* Create the queue used to send complete struct AMessage structures.  This can
   also be created after the schedule starts, but care must be task to ensure
   nothing uses the queue until after it has been created. */
   xStructQueue = xQueueCreate(
                         /* The number of items the queue can hold. */
                         10,
                         /* Size of each item is big enough to hold the
                         whole structure. */
                         sizeof( xMessage ) );

   /* Create the queue used to send pointers to struct AMessage structures. */
   xPointerQueue = xQueueCreate(
                         /* The number of items the queue can hold. */
                         10,
                         /* Size of each item is big enough to hold only a
                         pointer. */
                         sizeof( &xMessage ) );

   if( ( xStructQueue == NULL ) || ( xPointerQueue == NULL ) )
   {
      /* One or more queues were not created successfully as there was not enough
      heap memory available.  Handle the error here.  Queues can also be created
      statically. */
   }
}

/* Task that writes to the queues. */
void vATask( void *pvParameters )
{
struct AMessage *pxPointerToxMessage;

   /* Send the entire structure to the queue created to hold 10 structures. */
   xQueueSend( /* The handle of the queue. */
               xStructQueue,
               /* The address of the xMessage variable.  sizeof( struct AMessage )
               bytes are copied from here into the queue. */
               ( void * ) &xMessage,
               /* Block time of 0 says don't block if the queue is already full.
               Check the value returned by xQueueSend() to know if the message
               was sent to the queue successfully. */
               ( TickType_t ) 0 );

   /* Store the address of the xMessage variable in a pointer variable. */
   pxPointerToxMessage = &xMessage

   /* Send the address of xMessage to the queue created to hold 10    pointers. */
   xQueueSend( /* The handle of the queue. */
               xPointerQueue,
               /* The address of the variable that holds the address of xMessage.
               sizeof( &xMessage ) bytes are copied from here into the queue. As the
               variable holds the address of xMessage it is the address of xMessage
               that is copied into the queue. */
               ( void * ) &pxPointerToxMessage,
               ( TickType_t ) 0 );

   /* ... Rest of task code goes here. */
}

/* Task that reads from the queues. */
void vADifferentTask( void *pvParameters )
{
struct AMessage xRxedStructure, *pxRxedPointer;

   if( xStructQueue != NULL )
   {
      /* Receive a message from the created queue to hold complex struct AMessage
      structure.  Block for 10 ticks if a message is not immediately available.
      The value is read into a struct AMessage variable, so after calling
      xQueueReceive() xRxedStructure will hold a copy of xMessage. */
      if( xQueueReceive( xStructQueue,
                         &( xRxedStructure ),
                         ( TickType_t ) 10 ) == pdPASS )
      {
         /* xRxedStructure now contains a copy of xMessage. */
      }
   }

   if( xPointerQueue != NULL )
   {
      /* Receive a message from the created queue to hold pointers.  Block for 10
      ticks if a message is not immediately available.  The value is read into a
      pointer variable, and as the value received is the address of the xMessage
      variable, after this call pxRxedPointer will point to xMessage. */
      if( xQueueReceive( xPointerQueue,
                         &( pxRxedPointer ),
                         ( TickType_t ) 10 ) == pdPASS )
      {
         /* *pxRxedPointer now points to xMessage. */
      }
   }

   /* ... Rest of task code goes here. */
}

xQueueReceiveFromISR 从消息队列中读取消息 (中断中使用)

从队列中接收项目。从中断服务程序内使用此函数是安全的

BaseType_t xQueueReceiveFromISR
                (
                    QueueHandle_t xQueue,
                    void *pvBuffer,
                    BaseType_t *pxHigherPriorityTaskWoken
                );
参数:
xQueue 	要从中接收项目的队列的句柄。
pvBuffer 	指向缓冲区的指针,接收到的项目将被复制到这个缓冲区。
pxHigherPriorityTaskWoken 	任务可被阻塞,以等待队列可用空间。如果 xQueueReceiveFromISR 使得此类任务阻塞解除,*pxHigherPriorityTaskWoken 将设置为 pdTRUE,否则 *pxHigherPriorityTaskWoken 将保持不变。
从 FreeRTOSV7.3.0 开始,pxHigherPriorityTaskWoken 是一个可选参数,可设置为 NULL。

Returns:
如果从队列成功接收到项目,返回 pdTRUE,否则返回 pdFALSE

示例用法:
QueueHandle_t xQueue;

/* Function to create a queue and post some values. */
void vAFunction( void *pvParameters )
{
char cValueToPost;
const TickType_t xTicksToWait = ( TickType_t )0xff;

    /* Create a queue capable of containing 10 characters. */
    xQueue = xQueueCreate( 10, sizeof( char ) );
    if( xQueue == 0 )
    {
        /* Failed to create the queue. */
    }

    /* ... */

    /* Post some characters that will be used within an ISR.  If the queue
    is full then this task will block for xTicksToWait ticks. */
    cValueToPost = 'a';
    xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
    cValueToPost = 'b';
    xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );

    /* ... keep posting characters ... this task may block when the queue
    becomes full. */

    cValueToPost = 'c';
    xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
}

/* ISR that outputs all the characters received on the queue. */
void vISR_Routine( void )
{
BaseType_t xTaskWokenByReceive = pdFALSE;
char cRxedChar;

    while( xQueueReceiveFromISR( xQueue,
                                ( void * ) &cRxedChar,
                                &xTaskWokenByReceive) )
    {
        /* A character was received.  Output the character now. */
        vOutputCharacter( cRxedChar );

        /* If removing the character from the queue woke the task that was
        posting onto the queue xTaskWokenByReceive will have been set to
        pdTRUE.  No matter how many times this loop iterates only one
        task will be woken. */
    }

    if( xTaskWokenByReceive != pdFALSE )
    {
        /* We should switch context so the ISR returns to a different task.
        NOTE:  How this is done depends on the port you are using.  Check
        the documentation and examples for your port. */
        taskYIELD ();
    }
}

xQueueOverwrite 消息队列覆盖写入(任务中使用)

即使队列已满的情况下也将写入队列,同时覆盖队列中已经 存在的数据
xQueueOverwrite() 旨在用于长度为 1 的队列, 这意味着队列要么为空,要么为满

#define xQueueOverwrite( xQueue, pvItemToQueue ) \
    xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE )

参数:
xQueue  	接收发送数据的队列的句柄。
pvItemToQueue  	指向待入队数据项的指针。队列 队列可容纳的项数在 创建队列时定义,这些项 将从 pvItemToQueue 复制到队列存储区域。
Returns:
xQueueOverwrite() 是用于调用 xQueueGenericSend() 的宏, 
因此与 xQueueSendToFront() 具有相同的返回值。 不过,由于 xQueueOverwrite() 将写入队列(即使队列已满), pdPASS 是唯一可以返回的值

用法示例:
 void vFunction( void *pvParameters )
 {
	 QueueHandle_t xQueue;
	 unsigned long ulVarToSend, ulValReceived;

    /* Create a queue to hold one unsigned long value.  It is strongly
    recommended *not* to use xQueueOverwrite() on queues that can
    contain more than one value, and doing so will trigger an assertion
    if configASSERT() is defined. */
    xQueue = xQueueCreate( 1, sizeof( unsigned long ) );

    /* Write the value 10 to the queue using xQueueOverwrite(). */
    ulVarToSend = 10;
    xQueueOverwrite( xQueue, &ulVarToSend );

    /* Peeking the queue should now return 10, but leave the value 10 in
    the queue.  A block time of zero is used as it is known that the
    queue holds a value. */
    ulValReceived = 0;
    xQueuePeek( xQueue, &ulValReceived, 0 );

    if( ulValReceived != 10 )
    {
        /* Error, unless another task removed the value. */
    }

    /* The queue is still full.  Use xQueueOverwrite() to overwrite the
    value held in the queue with 100. */
    ulVarToSend = 100;
    xQueueOverwrite( xQueue, &ulVarToSend );

    /* This time read from the queue, leaving the queue empty once more.
    A block time of 0 is used again. */
    xQueueReceive( xQueue, &ulValReceived, 0 );

    /* The value read should be the last value written, even though the
    queue was already full when the value was written. */
    if( ulValReceived != 100 )
    {
        /* Error unless another task is using the same queue. */
    }

    /* ... */
}

xQueueOverwriteFromISR(中断中使用)

#define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \
    xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE )

参数:
xQueue  	接收发送数据的队列的句柄。
pvItemToQueue  	指向待入队数据项的指针。队列 队列可容纳的项数在 创建队列时定义,这些项 将从 pvItemToQueue 复制到队列存储区域。
pxHigherPriorityTaskWoken  	如果发送通知到队列导致某个任务解除阻塞, 且被解除阻塞的任务的优先级高于当前运行的任务, 则 xQueueOverwriteFromISR() 将把 *pxHigherPriorityTaskWoken 设置 为 pdTRUE。 如果 xQueueOverwriteFromISR() 将此值设置为 pdTRUE, 则应在中断退出之前请求上下文切换 。 请参阅所使用移植的文档的中断服务例程章节, 了解如何完成该操作。
Returns:
xQueueOverwriteFromISR() 是调用 xQueueGenericSendFromISR() 的宏, 因此与 xQueueSendToFrontFromISR() 具有 相同的返回值。 不过,由于 xQueueOverwriteFromISR() 将写入队列(即使队列已满), pdPASS 是唯一可以返回的值。

用法示例:
QueueHandle_t xQueue;

void vFunction( void *pvParameters )
{
    /* Create a queue to hold one unsigned long value.  It is strongly
    recommended not to use xQueueOverwriteFromISR() on queues that can
    contain more than one value, and doing so will trigger an assertion
    if configASSERT() is defined. */
    xQueue = xQueueCreate( 1, sizeof( unsigned long ) );
}

void vAnInterruptHandler( void )
{
/* xHigherPriorityTaskWoken must be set to pdFALSE before it is used. */
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
unsigned long ulVarToSend, ulValReceived;

    /* Write the value 10 to the queue using xQueueOverwriteFromISR(). */
    ulVarToSend = 10;
    xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );

    /* The queue is full, but calling xQueueOverwriteFromISR() again will still
    pass because the value held in the queue will be overwritten with the
    new value. */
    ulVarToSend = 100;
    xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );

    /* Reading from the queue will now return 100. */

    /* ... */

    if( xHigherPrioritytaskWoken == pdTRUE )
    {
        /* Writing to the queue caused a task to unblock and the unblocked task
        has a priority higher than or equal to the priority of the currently
        executing task (the task this interrupt interrupted).  Perform a
        context switch so this interrupt returns directly to the unblocked
        task. */
        portYIELD_FROM_ISR(); /* or portEND_SWITCHING_ISR() depending on the
        port.*/
    }
}

xQueuePeek 从队列中读取消息,而不从队列中删除该消息(任务中使用)

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

参数:
xQueue 	要从中接收项目的队列的句柄。
pvBuffer 	指针,指向将复制收到的项目的缓冲区。 它必须至少足够大,才能容纳创建队列时定义的队列项的大小。
xTicksToWait 	如果在调用时队列为空,则任务应阻塞等待项目接收的最长时间。 时间已在滴答周期中定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 来将其转换为实时。
如果 INCLUDE_vTaskSuspend设置为 “1”,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

Returns:
如果从队列中成功接收(窥视)项目,则返回 pdTRUE,否则返回 pdFALSE

xQueuePeekFromISR 从队列中读取消息,而不从队列中删除该消息(中断中使用)

BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue,
                              void * const pvBuffer );

参数:
xQueue  	要从中接收项目的队列的句柄。
pvBuffer  	指向复制接收项目缓冲区的指针 。 这必须至少足够大,以容纳队列创建时定义队列项的大小 。
Returns:
如果从队列中成功接收(窥视)项目,则返回 pdTRUE, 否则返回 pdFALSE

vQueueDelete 删除消息队列

void vQueueDelete( QueueHandle_t xQueue );

删除队列 — 释放分配用于存储放置在队列中的项目的所有内存。

参数:
xQueue  	要删除的队列的句柄

xQueueReset 重置队列为空状态

#define xQueueReset( xQueue )    xQueueGenericReset( ( xQueue ), pdFALSE )

将队列重置为其原始的空状态。

参数:
xQueue  	正在重置的队列的句柄

查询消息队列信息状态 API 函数

uxQueueMessagesWaiting 查询消息队列等待读取消息数量(任务中使用)

UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue );

参数:
xQueue  	正在查询的队列的句柄。

Returns:
队列中可用的消息数

uxQueueMessagesWaitingFromISR 查询消息队列等待读取消息数量(中断中使用)

UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue );

参数:
xQueue  	正在查询的队列的句柄。

Returns:
队列中可用的消息数

uxQueueSpacesAvailable 查询消息队列可用空间数量

UBaseType_t uxQueueSpacesAvailable( QueueHandle_t xQueue );

参数:
xQueue  	正在查询的队列的句柄。

Returns:
队列中可用的可用空间数

xQueueIsQueueEmptyFromISR 查询消息队列是否为空 (中断中使用)

此函数只能用于 ISR

BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue );

参数:
xQueue  	正在查询的队列的句柄
Returns:
如果队列不为空,则返回 pdFALSE;如果队列为空,则返回 pdTRUE

xQueueIsQueueFullFromISR 查询消息队列是否已满(中断中使用)

此函数只能用于 ISR

BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue );

参数:
xQueue  	正在查询的队列的句柄
Returns:
如果队列未满,则返回 pdFALSE;如果队列已满,则返回 pdTRUE
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值