ESP32 FreeRTOS-队列(7)

提示:好记性不如烂笔头。本博客作为学习笔记,有错误的地方希望指正

前言:

  参考资料:FreeRTOS API参考
  前面我们讲述了一个任务怎么创建,怎么使用以及对任务的调试,这里我们在开发的时候往往是多个任务执行的,这个时候我们就与这样的需求任务与任务之间的通讯,在没有操作系统的开发中我们需要不同.c文件之间的数据交互传输,我们一般使用全局变量,带有操作系统的开发中一般不推荐这么使用,这样使用可能会引起两个任务同时访问一个变量的时候出现问题,带操作系统任务通讯常用的就是我们的队列,队列的功能非常的强大。*

一、xQueueCreate()

API原型:

 QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
                             UBaseType_t uxItemSize );

  创建一个新队列并返回 可引用此队列的句柄。 configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中被设置为 1,或保留未定义状态(此时,它默认 为 1) ,才能使用此 RTOS API 函数。
  每个队列需要 RAM 用于保存队列状态和 包含在队列(队列存储区域)中的项目。 如果使用 xQueueCreate() 创建队列,则所需的 RAM 将自动 从 FreeRTOS 堆中分配。 如果使用 xQueueCreateStatic() 创建队列, 则 RAM 由应用程序编写者提供,这会产生更多的参数, 但这样能够在编译时静态分配 RAM 。 请参阅静态分配与 动态分配页面了解详情。

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

返回:
  如果队列创建成功,则返回所创建队列的句柄 。 如果创建队列所需的内存无法 分配 ,则返回 NULL。
  在创建队列的时候我们可以指定队列的长度以及每个长队的尺寸,我们可以使用sizeof()来获取队列长度,队列的空间所占大小=队列长度 X sizeof() 。队列的传输参数可以是任意的,我们可以传输整形、浮点型、数组、字符串、以及指针都可以。
使用示例:

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

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

    /* 创建一个能够包含10个无符号长值的队列。*/
    xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );

    if( xQueue1 == NULL )
    {
    
    
        /*队列未被创建,不得使用。*/
    }

    /* 创建一个能够包含10个AMessage结构指针的队列。
    结构的指针。 这些要用指针来排队,因为它们是
    相对较大的结构。*/
    xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );

    if( xQueue2 == NULL )
    {
    
    
        /*队列未被创建,不得使用。*/
    }
 }

二、xQueueCreateStatic()

API原型:

 QueueHandle_t xQueueCreateStatic(
                             UBaseType_t uxQueueLength,
                             UBaseType_t uxItemSize,
                             uint8_t *pucQueueStorageBuffer,
                             StaticQueue_t *pxQueueBuffer );

  创建一个新队列并返回 可以引用该队列的句柄。
  configSUPPORT_STATIC_ALLOCATION 必须在 FreeRTOSConfig.h 中设置为 1,该 RTOS API 函数才可用。
  每个队列都需要 RAM, 用于保存队列状态和队列中包含的数据项(队列存储区域)。 如果使用 xQueueCreate() 创建队列, 则会自动从 RAM 堆FreeRTOS中分配 。 如果使用 xQueueCreateStatic() 创建队列, 则 RAM 由应用程序编写者提供,这会产生更多的参数, 但这样能够在编译时静态分配 RAM 。 请参阅静态分配与 动态分配页面了解详情。

参数:
  uxQueueLength 队列可同时容纳的最大项目数 。
  uxItemSize 存储队列中的每个数据项所需的大小(以字节为单位)。
数据项按副本排队,而不是按引用排队, 因此该值为每个排队项目将被复制的字节数。队列中每个数据项 的大小必须相同。
  pucQueueStorageBuffer 如果 uxItemSize 非零,则 pucQueueStorageBuffer 必须 指向一个至少足够大的 uint8_t 数组, 以容纳队列中可以同时存在的最大项数, 即 (uxQueueLength * uxItemSize) 个字节。 如果 uxItemSize 为零,则 pucQueueStorageBuffer 可以为 Null。
  pxQueueBuffer 必须指向 StaticQueue_t 类型的变量, 该变量将用于保存队列的数据结构体。
返回:
  如果队列创建成功, 则返回已创建队列的句柄。 如果 pxQueueBuffer 为 NULL,则返回 NULL。
  xQueueCreateStatic()相比于xQueueCreate()他们两都是创建队列,但是我们没有特殊情况一般都使用的是xQueueCreate()来创建队列,使用xQueueCreateStatic()来创建队列的时候需要我们提供内存分配。
使用示例:

/* 该队列将被创建为最多容纳10个uint64_t变量。*/
#define QUEUE_LENGTH    10
#define ITEM_SIZE       sizeof( uint64_t )

/* 用来保存队列数据结构的变量。*/
static StaticQueue_t xStaticQueue;

/* 用作队列存储区域的阵列。 这必须至少是uxQueueLength * uxItemSize字节。*/
uint8_t ucQueueStorageArea[ QUEUE_LENGTH * ITEM_SIZE ];

void vATask( void *pvParameters )
{
    
    
	QueueHandle_t xQueue;
    /* 创建一个能够包含10个uint64_t值的队列。*/
    xQueue = xQueueCreateStatic( QUEUE_LENGTH,
                                 ITEM_SIZE,
                                 ucQueueStorageArea,
                                 &xStaticQueue );

    /* pxQueueBuffer不是NULL,所以xQueue不应该是NULL。*/
    configASSERT( xQueue );
 }

三、vQueueDelete()

API原型:

void vQueueDelete( QueueHandle_t xQueue );

  删除队列 — 释放分配用于存储放置在队列中的项目的所有内存。
参数:
  xQueue 要删除的队列的句柄。

四、xQueueSend()

API原型:

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

  这是一个调用 xQueueGenericSend() 的宏。 它用于向后兼容 FreeRTOS 的一些版本, 这些版本不包含 xQueueSendToFront() 和 xQueueSendToBack() 宏。 它 等同于 xQueueSendToBack()。
  在队列中发布项目。该项目按副本排队,而不是按引用排队。不得从中断服务程序调用此函数。请参阅 xQueueSendFromISR() 以获取 可用于 ISR 的替代方案。
参数:
  xQueue 队列的句柄,数据项将发布到此队列。
  pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
  xTicksToWait 如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果队列已满,并且 xTicksToWait 设置为0 ,调用将立即返回。时间在 tick 周期中定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。
如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。
返回:
  如果成功发布项目,返回 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;
	
	/* 创建一个能够包含10个无符号长值的队列。*/
	xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
	
	/* 创建一个队列,能够包含10个指向AMessage结构的指针。
	这些应该通过指针传递,因为它们包含了大量的数据。*/
	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
    /* ... */
    if( xQueue1 != 0 )
    {
    
    
        /* 发送一个无符号长信息。 如果有必要的话,等待10秒钟,让空间变得
        如果需要的话。*/
        if( xQueueSend( xQueue1,
                       ( void * ) &ulVar,
                       ( TickType_t ) 10 ) != pdPASS )
        {
    
    
            /* 发布信息失败,即使在10个刻度之后。*/
        }
    }

    if( xQueue2 != 0 )
    {
    
    
        /* 发送一个指向AMessage结构对象的指针。 如果队列已经满了,就不要阻塞。
        队列已经满了。*/
        pxMessage = & xMessage;
        xQueueSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 )}
	/* ... 任务代码的其余部分。*/
 }

五、xQueueSendFromISR()

API原型:

BaseType_t xQueueSendFromISR
          (
              QueueHandle_t xQueue,
              const void *pvItemToQueue,
              BaseType_t *pxHigherPriorityTaskWoken);

  这是用于调用 xQueueGenericSendFromISR() 的宏。 加入此宏可 向后兼容不包括 xQueueSendToBackFromISR() 和 xQueueSendToFrontFromISR() 宏的 FreeRTOS 版本 。
  将项目发布到队列尾部。在中断服务程序中使用此函数是安全的。
  数据项通过复制而非引用入队,因此最好只将较小的项放入队列, 特别是从 ISR 调用时。在大多数情况下,最好存储一个指向正在排队的数据项的指针。
参数:
  xQueue 队列的句柄,数据项将发布到此队列。
  pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
  pxHigherPriorityTaskWoken 如果发送到队列导致任务解除阻塞,且解除阻塞的任务的优先级高于当前运行的任务,则xQueueSendFromISR() 会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。 如果 xQueueSendFromISR() 将此值设置为 pdTRUE,则应在中断退出前请求上下文切换。
  从 FreeRTOS V7.3.0 开始,pxHigherPriorityTaskWoken 是一个可选参数,可设置为 NULL。
返回:
  如果将数据成功发送至队列,则返回 pdTRUE,否则返回 errQUEUE_FULL。
缓冲 IO(每次调用 ISR 可以获取多个值)的用法示例:
使用示例:

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

    /* 我们在ISR开始时没有唤醒一个任务。*/
    xHigherPriorityTaskWoken = pdFALSE;

    /* 循环直到缓冲区为空。*/
    do
    {
    
    
        /* 从缓冲区获取一个字节。*/
        cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS )/* 发布该字节。*/
        xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken )} while( portINPUT_BYTE( BUFFER_COUNT ))/* 现在缓冲区是空的,如果需要,我们可以切换上下文。*/
    if( xHigherPriorityTaskWoken )
    {
    
    
        /* 这里使用的实际宏是针对端口的。*/
        taskYIELD_FROM_ISR()。
    }
}

六、xQueueSendToBack()

API原型:

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

  这是一个调用 xQueueGenericSend() 的宏。 它等同于 xQueueSend()。
  从队列尾部入队一个数据项。 数据项通过复制 而非引用入队。 不得从中断服务程序 。 请参阅 xQueueSendToBackFromISR (),获取可在 ISR 中使用的 替代方案。

参数:
  xQueue 队列的句柄,数据项将发布到此队列。
  pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
  xTicksToWait 如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果设置为 0,调用将立即返回。时间以滴答周期为单位定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。
如果 INCLUDE_vTaskSuspend设置为 “1”,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。
返回:
  如果成功发布项目,返回 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;
	/* 创建一个能够包含10个无符号长值的队列。*/
    xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) )/* 创建一个队列,能够包含10个指向AMessage结构的指针。
    结构的指针。 这些应该通过指针传递,因为它们包含大量的
    数据。*/
    xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) )/* ... */
    if( xQueue1 != 0 )
    {
    
    
        /* 发送一个无符号长信息。 如果有必要的话,等待10秒钟,让空间变得
        如果需要的话。*/
        if( xQueueSendToBack( xQueue1,
                             ( void * ) &ulVar,
                             ( TickType_t ) 10 ) != pdPASS )
        {
    
    
            /* 发布信息失败,即使在10个刻度之后。*/
        }
    }

    if( xQueue2 != 0 )
    {
    
    
        /* 发送一个指向AMessage结构对象的指针。 如果队列已经满了,就不要阻塞。
        队列已经满了。*/
        pxMessage = & xMessage;
        xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 )}
	/* ... 任务代码的其余部分。*/
}

七、xQueueSendToBackFromISR()

API原型:

BaseType_t xQueueSendToBackFromISR(
			                       QueueHandle_t xQueue,
			                       const void *pvItemToQueue,
			                       BaseType_t *pxHigherPriorityTaskWoken);

  此宏用于调用 xQueueGenericSendFromISR()。
  从队列尾部入队一个数据项。可在中断服务程序中使用此函数。
  数据项通过复制而非引用入队,因此最好只发送较小的项,尤其是从 ISR 调用时。
参数:
  xQueue 队列的句柄,数据项将发布到此队列。
  pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
  pxHigherPriorityTaskWoken 如果发送到队列导致某个任务解除阻塞,并且解除阻塞的任务的优先级高于当前运行的任务,则 xQueueSendTobackFromISR() 会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。 如果 xQueueSendTobackFromISR() 将此值设置为 pdTRUE,则应在退出中断之前请求上下文切换。
  从 FreeRTOSV7.3.0 开始,pxHigherPriorityTaskWoken 是一个可选参数,可设置为 NULL。
返回:
  若成功发送至队列,则返回 pdPASS,否则返回 errQUEUE_FULL。
  缓冲 IO(每次调用 ISR 可以获取多个值)的用法示例:
使用示例:

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

    /* 我们在ISR开始时没有唤醒一个任务。*/
    xHigherPriorityTaskWoken = pdFALSE;

    /* 循环直到缓冲区为空。*/
    do
    {
    
    
        /* 从缓冲区获取一个字节。*/
        cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS )/* 发布该字节。*/
        xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken )} while( portINPUT_BYTE( BUFFER_COUNT ))/* 现在缓冲区是空的,如果需要,我们可以切换上下文。*/
    if( xHigherPriorityTaskWoken )
    {
    
    
        /* 这里使用的实际宏是针对端口的。*/
        taskYIELD_FROM_ISR()。
    }
}

八、xQueueSendToFront()

API原型:

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

  此宏用于调用 xQueueGenericSend()。
  从队列头部入队一个数据项。 数据项通过复制 而非引用入队。 不得从中断服务程序 调用此函数。 请参阅 xQueueSendToFrontFromISR() 了解 可在 ISR 中使用的替代方法。
参数:
  xQueue 队列的句柄,数据项将发布到此队列。
  pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
  xTicksToWait 如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果设置为 0,调用将立即返回。时间以滴答周期为单位定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。
如果 INCLUDE_vTaskSuspend设置为 “1”,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。
返回:
  如果成功发布项目,返回 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;

    /* 创建一个能够包含10个无符号长值的队列。*/
    xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) )/* 创建一个队列,能够包含10个指向AMessage结构的指针。
    结构的指针。 这些应该通过指针传递,因为它们包含大量的
    数据。*/
    xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) )/* ... */

    if( xQueue1 != 0 )
    {
    
    
        /* 发送一个无符号长信息。 如果有必要的话,等待10秒钟,让空间变得
        如果需要的话。*/
        if( xQueueSendToFront( xQueue1,
                              ( void * ) &ulVar,
                              ( TickType_t ) 10 ) != pdPASS )
        {
    
    
            /* 发布信息失败,即使是在10个tick之后。*/
        }
    }

    if( xQueue2 != 0 )
    {
    
    
        /* 发送一个指向AMessage结构对象的指针。 如果队列已经满了,就不要阻塞。
        队列已经满了。*/
        pxMessage = &xMessage;
        xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 )}

	/* ... 任务代码的其余部分。*/
}

九、xQueueSendToFrontFromISR()

API原型:

BaseType_t xQueueSendToFrontFromISR(
                    QueueHandle_t xQueue,
                    const void *pvItemToQueue,
                    BaseType_t *pxHigherPriorityTaskWoken);

  这是用于调用 xQueueGenericSendFromISR() 的宏。
  从队列头部入队一个数据项。可在中断服务程序中使用此函数。
  数据项通过复制而非引用入队,因此最好只发送较小的项,或者发送指向该项的指针。
参数:
  xQueue 队列的句柄,数据项将发布到此队列。
  pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
  pxHigherPriorityTaskWoken 如果发送到队列导致某个任务解除阻塞,且被解除阻塞的任务的优先级高于当前运行的任务,则 xQueueSendToFrontFromISR() 会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。 如果 xQueueSendToFrontFromISR() 将此值设置为 pdTRUE,则应在中断退出前请求上下文切换。
从 FreeRTOS V7.3.0 开始,pxHigherPriorityTaskWoken 是一个可选参数,可设置为 NULL。
返回:
  若数据成功发送至队列,则返回 pdPass,否则返回 errQUEUE_FULL。

十、xQueueReceive()

API原型:

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

  从队列接收一个项目。这个项目通过复制接收,因此缓冲器必须提供足够大的空间。复制进缓冲器的字节数,在队列创建时已经定义。
  这个函数一定不能在中断服务程序中使用。参考 xQueueReceiveFromISR 获得能够的选择。
参数:
  pxQueue 将要接收项目的队列句柄
  pvBuffer 指向将要复制接收项目的缓冲器的指针。
  xTicksToWait 任务中断并等待队列中可用空间的最大时间,应该是满的。如果设置为0,调用将立刻返回。时间在片区间中定义,如果需要,portTICK_RATE_MS常量用来转换为实际时间。
如果 INCLUDE_vTaskSuspend 定义为1 ,指定的中断时间( portMAX_DELAY) 将导致任务无限期中断(没有时间溢出)。
返回:
  如果从队列成功接收到项目,返回 pdTRUE,否则返回 pdFALSE。

十一、xQueueReceiveFromISR()

API原型:

BaseType_t xQueueReceiveFromISR(
                   QueueHandle_t xQueue,
                   void *pvBuffer,
                   BaseType_t *pxHigherPriorityTaskWoken);

  从队列中接收项目。从中断服务程序内使用此函数是安全的。
参数:
  xQueue 要从中接收项目的队列的句柄。
  pvBuffer 指向缓冲区的指针,接收到的项目将被复制到这个缓冲区。
  pxHigherPriorityTaskWoken 任务可被阻塞,以等待队列可用空间。如果 xQueueReceiveFromISR 使得此类任务阻塞解除,*pxHigherPriorityTaskWoken 将设置为 pdTRUE,否则 *pxHigherPriorityTaskWoken 将保持不变。
从 FreeRTOSV7.3.0 开始,pxHigherPriorityTaskWoken 是一个可选参数,可设置为 NULL。
返回:
  如果从队列成功接收到项目,返回 pdTRUE,否则返回 pdFALSE。
使用示例:

QueueHandle_t xQueue。
/* 创建一个队列并发布一些值的函数。*/
void vAFunction( void *pvParameters )
{
    
    
	char cValueToPost;
	const TickType_t xTicksToWait = ( TickType_t )0xff;

    /* 创建一个能够包含10个字符的队列。*/
    xQueue = xQueueCreate( 10, sizeof( char ) )if( xQueue == 0 )
    {
    
    
        /* 创建队列失败。*/
    }

    /* ... */

    /* 发布一些将在ISR中使用的字符。 如果队列
    则该任务将阻塞xTicksToWait ticks。*/
    cValueToPost = 'a';
    xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait ) 。
    cValueToPost = 'b';
    xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait )/* ...继续发布字符...当队列变成满的时候,这个任务可能会阻塞。
    成为满的时候,这个任务可能会阻塞。*/

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

/* 输出队列中收到的所有字符的ISR。*/
void vISR_Routine( void )
{
    
    
	BaseType_t xTaskWokenByReceive = pdFALSE。
	char cRxedChar;
    while( xQueueReceiveFromISR( xQueue,
                                ( void * ) &cRxedChar,
                                &xTaskWokenByReceive) )
    {
    
    
        /* 收到了一个字符。 现在输出该字符。*/
        vOutputCharacter( cRxedChar )/* 如果从队列中移除字符,那么在队列中发帖的任务xTaskWokenByReceive将被设置为
        xTaskWokenByReceive将被设置为
        pdTRUE。 无论这个循环迭代多少次,只有一个任务被唤醒。
        任务都会被唤醒。*/
    }

    if( xTaskWokenByReceive != pdFALSE )
    {
    
    
        /* 我们应该切换上下文,以便ISR返回到一个不同的任务。
        注意:如何做到这一点取决于你所使用的端口。 检查
        你的端口的文档和例子。*/
        taskYIELD ()}
}

十二、uxQueueMessagesWaiting()

API原型:

UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue );

  返回队列中存储的消息数。
参数:
  xQueue 正在查询的队列的句柄。
返回:
  队列中可用的消息数。

十三、uxQueueMessagesWaitingFromISR()

API原型:

UBaseType_t uxQueueMessagesWaitingFromISR( QueueHandle_t xQueue );

  可从 ISR 中调用的 uxQueueMessagesWaiting() 的一个版本。 返回 队列中存储的消息数。
参数:
  xQueue 正在查询的队列的句柄。
返回:
  队列中可用的消息数。

十四、uxQueueSpacesAvailable()

API原型:

UBaseType_t uxQueueSpacesAvailable( QueueHandle_t xQueue );

  返回队列中的可用空间数。
参数:
  xQueue 正在查询的队列的句柄。
返回:
  队列中可用的可用空间数。

十五、xQueueReset()

API原型:

BaseType_t xQueueReset( QueueHandle_t xQueue );

  将队列重置为其原始的空状态。
参数:
  xQueue 正在重置的队列的句柄
返回:
  因为 FreeRTOSV7.2.0 xQueueReset() 总是返回 pdPASS。

十六、xQueueOverwrite()

API原型:

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

  这是用于调用 xQueueGenericSend() 函数的宏。
  即使队列已满的情况下也将写入队列的 xQueueSendToBack() 版本, 同时覆盖队列中已经 存在的数据。
  xQueueOverwrite() 旨在用于长度为 1 的队列, 这意味着队列要么为空,要么为满。
  不得从中断服务程序 (ISR) 调用此函数。 请参阅 xQueueOverwriteFromISR(),了解可在 ISR 中使用的替代函数。
参数:
  xQueue 接收发送数据的队列的句柄。
  pvItemToQueue 指向待入队数据项的指针。队列 队列可容纳的项数在 创建队列时定义,这些项将从 pvItemToQueue 复制到队列存储区域。
返回:
  xQueueOverwrite() 是用于调用 xQueueGenericSend() 的宏, 因此与 xQueueSendToFront() 具有相同的返回值。 不过,由于 xQueueOverwrite() 将写入队列(即使队列已满), pdPASS 是唯一可以返回的值。

使用示例:

 void vFunction( void *pvParameters )
 {
    
    
	 QueueHandle_t xQueue;
	 unsigned long ulVarToSend, ulValReceived;

    /* 创建一个队列来容纳一个无符号长值。 强烈建议
    强烈建议*不要*在可以包含多个值的队列上使用xQueueOverwrite()。
    强烈建议不要在可以包含一个以上的值的队列上使用xQueueOverwrite(),这样做会触发一个断言。
    如果定义了configASSERT()。*/
    xQueue = xQueueCreate( 1, sizeof( unsigned long ) )/* 使用xQueueOverwrite()将数值10写到队列中。*/
    ulVarToSend = 10;
    xQueueOverwrite( xQueue, &ulVarToSend )/* 窥视队列现在应该返回10,但在队列中留下数值10。
    的值留在队列中。 块时间为0,因为我们知道队列里有一个值。
    队列中有一个值。*/
    ulValReceived = 0;
    xQueuePeek( xQueue, &ulValReceived, 0 )if( ulValReceived != 10 )
    {
    
    
        /* 错误,除非另一个任务删除了该值。*/
    }

    /* 队列仍然是满的。 使用xQueueOverwrite()来覆盖
    用100来覆盖队列中的值。*/
    ulVarToSend = 100;
    xQueueOverwrite( xQueue, &ulVarToSend )/* 这一次从队列中读出,让队列再次变空。
    再次使用一个0的阻塞时间。*/
    xQueueReceive( xQueue, &ulValReceived, 0 )/* 读取的值应该是最后写入的值,即使队列中的值已经满了。
    读取的值应该是最后写入的值,即使在写入该值时队列已经满了。*/
    if( ulValReceived != 100 )
    {
    
    
        /* 错误,除非另一个任务正在使用同一个队列。*/
    }
    /* ... */
}

十七、xQueueOverwriteFromISR()

API原型:

 BaseType_t xQueueOverwriteFromISR(
                      QueueHandle_t xQueue,
                      const void * pvItemToQueue
                      BaseType_t *pxHigherPriorityTaskWoken);

  这是用于调用 xQueueGenericSend() 函数的宏。
  即使队列已满的情况下也将写入队列的 xQueueSendToBack() 版本, 同时覆盖队列中已经 存在的数据。
  xQueueOverwrite() 旨在用于长度为 1 的队列, 这意味着队列要么为空,要么为满。
  不得从中断服务程序 (ISR) 调用此函数。 请参阅 xQueueOverwriteFromISR(),了解可在 ISR 中使用的替代函数。
参数:
  xQueue 接收发送数据的队列的句柄。
  pvItemToQueue 指向待入队数据项的指针。队列 队列可容纳的项数在 创建队列时定义,这些项 将从 pvItemToQueue 复制到队列存储区域。
返回:
  xQueueOverwrite() 是用于调用 xQueueGenericSend() 的宏, 因此与 xQueueSendToFront() 具有相同的返回值。 不过,由于 xQueueOverwrite() 将写入队列(即使队列已满), pdPASS 是唯一可以返回的值。

使用示例:

QueueHandle_t xQueue。

void vFunction( void *pvParameters )
{
    
    
    /* 创建一个队列来容纳一个无符号长值。 强烈建议不要使用
    强烈建议不要在可以包含多个值的队列上使用xQueueOverwriteFromISR()。
    强烈建议不要在可以包含一个以上的值的队列上使用xQueOverwriteFromISR(),这样做会触发一个断言。
    如果定义了configASSERT()。*/
    xQueue = xQueueCreate( 1, sizeof( unsigned long ) )}

void vAnInterruptHandler( void )
{
    
    
	/* xHigherPriorityTaskWoken在使用前必须被设置为pdFALSE。*/
	BaseType_t xHigherPriorityTaskWoken = pdFALSE。
	unsigned long ulVarToSend, ulValReceived;

    /* 使用xQueueOverwriteFromISR()将值10写到队列中。*/
    ulVarToSend = 10;
    xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken )/* 队列已满,但再次调用xQueueOverwriteFromISR()仍会通过,因为队列中持有的值。
    通过,因为队列中的值将被新的值覆盖。
    新值覆盖。*/
    ulVarToSend = 100;
    xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken )/* 从队列中读取数据现在将返回100。*/

    /* ... */

    if( xHigherPrioritytaskWoken == pdTRUE )
    {
    
    
        /* 写入队列导致一个任务解除封锁,并且解除封锁的任务
        的优先级高于或等于当前执行任务的优先级。
        执行的任务(该中断所中断的任务)的优先级。 执行一个
        执行上下文切换,使该中断直接返回到未阻塞的
        任务。*/
        portYIELD_FROM_ISR(); /*或portEND_SWITCHING_ISR(),具体取决于
        */取决于端口。
    }
}

十八、xQueuePeek()

API原型:

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

  这是一个调用 xQueueGenericReceive() 函数的宏。
  从队列中接收项目,而无须从队列中删除该项目。 项目由副本接收,因此必须提供适当大小的缓冲区 。 队列创建时,复制到缓冲区中的字节数已定义 。
  成功接收的项目仍在队列中,因此将由下一次调用再次返回 或 xQueueReceive () 调用。
  中断服务例程中不得使用此宏。
参数:
  xQueue 要从中接收项目的队列的句柄。
  pvBuffer 指针,指向将复制收到的项目的缓冲区。 它必须至少足够大,才能容纳创建队列时定义的队列项的大小。
  xTicksToWait 如果在调用时队列为空,则任务应阻塞等待项目接收的最长时间。 时间已在滴答周期中定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 来将其转换为实时。
如果 INCLUDE_vTaskSuspend设置为 “1”,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。
返回:
  如果从队列中成功接收(窥视)项目,则返回 pdTRUE,否则返回 pdFALSE。
使用示例:

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

 QueueHandle_t xQueue。

 // 创建一个队列并发布一个值的任务。
 void vATask( void *pvParameters )
 {
    
    
 	struct AMessage *pxMessage;
    // 创建一个队列,能够包含10个指向AMessage结构的指针。
    // 这些应该用指针传递,因为它们包含大量的数据。
    xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
    if( xQueue == 0 )
    {
    
    
        // 创建队列失败。
    }

    // ...

    // 发送一个指向AMessage结构对象的指针。 如果队列已经满了,请不要阻塞。
    // queue is already full.
    pxMessage = &xMessage;
    xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );

    // ... 任务代码的其余部分。
 }

 // 从队列中偷看数据的任务。
 void vADifferentTask( void *pvParameters )
 {
    
    
 	struct AMessage *pxRxedMessage;
    if( xQueue != 0 )
    {
    
    
        // 在创建的队列中偷看一条消息。 如果一个
        // 消息不是立即可用的。
        if( xQueuePeek( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
        {
    
    
            // pcRxedMessage现在指向vATask发布的AMessage结构变量。
            // 由vATask发布,但该项目仍在队列中。
        }
    }

    // ... 任务代码的其余部分。
 }

十九、xQueuePeekFromISR()

API原型:

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

  xQueuePeek() 的版本,可以从 中断服务程序 (ISR) 内部分配。
  从队列中接收项目,而无须从队列中删除该项目。 项目由副本接收,因此必须提供适当大小的缓冲区 。 队列创建时,复制到缓冲区中的字节数已定义 。
  成功接收的项目仍在队列中,因此将由下个调用再次返回 或由任何队列接收函数调用的返回。
参数:
  xQueue 要从中接收项目的队列的句柄。
  pvBuffer 指向复制接收项目缓冲区的指针 。 这必须至少足够大,以容纳队列创建时定义队列项的大小 。
返回:
  如果从队列中成功接收(窥视)项目,则返回 pdTRUE, 否则返回 pdFALSE。

二十、vQueueAddToRegistry()

API原型:

void vQueueAddToRegistry(
                            QueueHandle_t xQueue,
                            char *pcQueueName);

  为队列指定名称,并将队列添加到注册表。
参数:
  xQueue 添加到注册表的队列句柄。
  pcQueueName 指定的队列名称。 此为文本字符串,仅为便于调试之用。队列注册表仅存储指向该字符串的指针,因此该字符串必须具有持久性(全局变量,或最好是在 ROM/Flash 中),而不是在堆栈上定义。
  队列注册表有两项用途,都与 RTOS 内核感知调试相关:

  1. 可以关联文本名称和队列,便于在调试 GUI 中识别队列。
  2. 包含调试器定位每个已注册队列和信号量所需的信息。

  除非使用 RTOS 内核感知调试器,否则队列注册表没有任何用途。
  configQUEUE_REGISTRY_SIZE 定义了可以注册的队列和信号量的最大数量。 仅需注册那些要使用 RTOS 内核感知调试器查看的队列和信号量。
使用示例:

void vAFunction( void )
{
    
    
	QueueHandle_t xQueue;
	/* 创建一个足够大的队列,以容纳10个字符。*/
	xQueue = xQueueCreate( 10, sizeof( char ) );
	/* 我们希望这个队列可以在RTOS内核感知的调试器中查看。
	所以要注册它。*/
	vQueueAddToRegistry( xQueue, "AMeaningfulName");
}

二一、vQueueUnregisterQueue()

API原型:

void vQueueUnregisterQueue(QueueHandle_t xQueue);

  从队列注册表中删除队列。
参数:
  xQueue 从注册表中删除的队列句柄。
  队列注册表有两个目的,都与 RTOS 内核感知调试相关:

  • 可以关联文本名称和队列,便于在调试 GUI 中识别队列。
  • 包含调试器定位每个已注册队列和信号量所需的信息。

  除非使用 RTOS 内核感知调试器,否则队列注册表没有任何用途。
  configQUEUE_REGISTRY_SIZE 定义了可以注册的队列和信号量的最大数量。 仅需注册那些要使用 RTOS 内核感知调试器查看的队列和信号量。
使用示例:

void vAFunction( void )
{
    
    
	QueueHandle_t xQueue;
	
	/* 创建一个足够大的队列,以容纳10个字符。*/
	xQueue = xQueueCreate( 10, sizeof( char ) );
	
	/* 我们希望这个队列可以在RTOS内核感知的调试器中查看。
	所以要注册它。*/
	vQueueAddToRegistry( xQueue, "AMeaningfulName");
	
	/* 该队列在这里被使用。*/
	
	/* 在以后的某个时候,如果要删除这个队列,首先要把它从注册表中删除。
	从注册表中删除它。*/
	vQueueUnregisterQueue( xQueue );
	vQueueDelete( xQueue );
}

二二、pcQueueGetName()

API原型:

const char *pcQueueGetName( QueueHandle_t xQueue )

  从队列的句柄中查找队列名称。
  队列只有添加到队列注册表时才有名称。
参数:
  xQueue 正在查询的队列的句柄。
返回:
  如果 xQueue 引用的队列在队列注册表中, 则返回队列的文本名称,否则返回 NULL。

二 三、xQueueIsQueueFullFromISR()

API原型:

BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue );

  查询队列以确定队列是否已满。 此函数只能用于 ISR。
参数:
  xQueue 正在查询的队列的句柄。
返回:
  如果队列未满,则返回 pdFALSE;如果队列已满,则返回 pdTRUE。

二四 、xQueueIsQueueEmptyFromISR()

API原型:

BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue );

  查询队列以确定队列是否为空。 此函数只能用于 ISR。
参数:
  xQueue 正在查询的队列的句柄
Returns:
  如果队列不为空,则返回 pdFALSE;如果队列为空,则返回 pdTRUE。

二五 、队列综合示例代码

队列基本API

/**
 * @file 5_TaskControl.c
 * @author WSP
 * @brief 任务队列
 * @version 0.1
 * @date 2022-10-09
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_Task_Queue";

// 根据宏定义来修改测试代码中不同部分
#define USER_DATA_TYPE_POINT 1

#if USER_DATA_TYPE_INT
typedef int INT_Data;
#elif USER_DATA_TYPE_ARRAY
typedef char Array_Data[6];
#elif USER_DATA_TYPE_POINT
typedef char * Pstr_Data;
#elif USER_DATA_TYPE_STRCUT
typedef struct  Struct_Data_t
{
    
    
    int Name_Number;
    char * Name;
}Struct_Data;
#endif

/**
 * @brief APP_Task_Queue_Send
 * @param arg 任务传入的参数
 * @return NULL
 */
void APP_Task_Queue_Send(void *arg)
{
    
    
    QueueHandle_t QueueHandle = (QueueHandle_t)arg;
     BaseType_t SendState;
    while (1)
    {
    
    
        vTaskDelay(2000 / portTICK_PERIOD_MS);
#ifdef USER_DATA_TYPE_INT
        INT_Data data = 168;                                                    // 发送的数据
        SendState = xQueueSend(QueueHandle, &data, 10 / portTICK_PERIOD_MS);    // 发送队列
        if(SendState != pdPASS)
            ESP_LOGI(TAG,"data send fail");
#elif USER_DATA_TYPE_ARRAY
        Array_Data data = {
    
    1, 2, 3, 4, 99, 168};
        SendState = xQueueSend(QueueHandle, &data, 10 / portTICK_PERIOD_MS);
        if(SendState != pdPASS)
            ESP_LOGI(TAG,"data send fail");
#elif USER_DATA_TYPE_POINT
        Pstr_Data data = (char *) malloc(25);
        snprintf(data,25,"USER_DATA_TYPE_STRING");
        SendState = xQueueSend(QueueHandle, &data, 10 / portTICK_PERIOD_MS);
        if(SendState != pdPASS)
            ESP_LOGI(TAG,"data send fail");
#elif USER_DATA_TYPE_STRCUT
        Struct_Data data = {
    
    1,"WSP"};
        SendState = xQueueSend(QueueHandle, &data, 10 / portTICK_PERIOD_MS);
        if(SendState != pdPASS)
            ESP_LOGI(TAG,"data send fail");
#endif
    }
}
/**
 * @brief   APP_Task_Queue_Get
 * @param   NULL
 * @return  NULL
 */
void APP_Task_Queue_Get(void *arg)
{
    
    
    QueueHandle_t QueueHandle = (QueueHandle_t)arg;
    BaseType_t SendState;
    int QueueDataLen = 0;
    while (1)
    {
    
    
        vTaskDelay(2000 / portTICK_PERIOD_MS);
#ifdef USER_DATA_TYPE_INT
        INT_Data data;
        QueueDataLen = uxQueueMessagesWaiting(QueueHandle);                         // 获取队列接受情况 返回队列长度    
        if(QueueDataLen != 0){
    
                                                          // 接收长度大于零的时候        
            SendState = xQueueReceive(QueueHandle, &data, 10 / portTICK_PERIOD_MS); // 获取接受状态        
            if(SendState != pdPASS)                                                 // 接受状态
                ESP_LOGI(TAG,"data send fail");
            else
                ESP_LOGI(TAG, "Int type Queue Receive DATA:%d data len :%d", data,QueueDataLen);
        }
#elif USER_DATA_TYPE_ARRAY
        Array_Data data;
        QueueDataLen = uxQueueMessagesWaiting(QueueHandle); // 返回存储在队列中的消息数。
        if(QueueDataLen != 0){
    
    
            SendState = xQueueReceive(QueueHandle, &data, 10 / portTICK_PERIOD_MS);
            if(SendState != pdPASS){
    
    
                ESP_LOGI(TAG,"data send fail");
            }else{
    
    
                for (uint8_t i = 0; i < 6; i++){
    
    
                    ESP_LOGI(TAG, "Array Type Queue Receive DATA:%d data len :%d", data[i],QueueDataLen);
                }
            }
        }
#elif USER_DATA_TYPE_POINT
        Pstr_Data data;
        QueueDataLen = uxQueueMessagesWaiting(QueueHandle);
        if(QueueDataLen != 0){
    
    
            SendState = xQueueReceive(QueueHandle, &data, 10 / portTICK_PERIOD_MS);
            if(SendState != pdPASS)
                ESP_LOGI(TAG,"data send fail");
            else
                ESP_LOGI(TAG, "Array Type Queue Receive DATA:%s data len :%d", data, QueueDataLen);
        }
#elif USER_DATA_TYPE_STRCUT
        Struct_Data data;
        QueueDataLen = uxQueueMessagesWaiting(QueueHandle);
        if(QueueDataLen != 0){
    
    
            SendState = xQueueReceive(QueueHandle, &data, 10 / portTICK_PERIOD_MS);
            if(SendState != pdPASS)
                ESP_LOGI(TAG,"data send fail");
            else
                ESP_LOGI(TAG, "Struct Type Queue Receive DATA name number:%d name:%s data len :%d", data.Name_Number,data.Name, QueueDataLen);
        }
#endif
    }
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void Task_Queue_Init(void)
{
    
    
    TaskHandle_t APP_Task_Queue_Handle = NULL;

#ifdef USER_DATA_TYPE_INT                                           // 队列传输整型数据
    APP_Task_Queue_Handle = xQueueCreate(6, sizeof(INT_Data));      // 创建队列
#elif USER_DATA_TYPE_ARRAY                                          // 队列传输数组
    APP_Task_Queue_Handle = xQueueCreate(6, sizeof(Array_Data));    // 创建队列
#elif USER_DATA_TYPE_POINT                                          // 队列传输指针   
    APP_Task_Queue_Handle = xQueueCreate(6, sizeof(Pstr_Data));     // 创建队列
#elif USER_DATA_TYPE_STRCUT                                         // 队列传输结构体
    APP_Task_Queue_Handle = xQueueCreate(6, sizeof(Struct_Data));   // 创建队列
#endif

    if (APP_Task_Queue_Handle != NULL)
    {
    
    
        // 创建任务一
        xTaskCreate(APP_Task_Queue_Send,    // 创建任务
                    "Task_Queue_Send",      // 创建任务名
                    2048,                   // 任务堆栈
                    (void *)APP_Task_Queue_Handle,
                    3,                      // 默认3
                    NULL);                  // 任务句柄
        // 创建任务二
        xTaskCreate(APP_Task_Queue_Get,     // 创建任务
                    "Task_Queue_Get",       // 创建任务名
                    2048,                   // 任务堆栈
                    (void *)APP_Task_Queue_Handle,
                    2,                      // 默认3
                    NULL);                  // 任务句柄
        ESP_LOGI(TAG, "Task create ok");
    }
    else
    {
    
    
        ESP_LOGI(TAG, "Task create fail");
    }
    vTaskDelay(3000 / portTICK_PERIOD_MS); // 延时等待
}

队列的多进一出 多个通道进入一个通道输出

/**
 * @file 8_TaskQueue_More_Input_One_Output.c
 * @author WSP
 * @brief 队列的多进一出 多个通道进入一个通道输出
 * @version 0.1
 * @date 2022-10-18
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_TaskQueue_More_Input_One_Output";



/**
 * @brief APP_Task_Queue_Send_One
 * @param arg 任务传入的参数
 * @return NULL
 */
void APP_Task_Queue_Send_One(void *arg)
{
    
    
    QueueHandle_t QueueHandle = (QueueHandle_t)arg;
     BaseType_t SendState;
    while (1)
    {
    
    
        vTaskDelay(2000 / portTICK_PERIOD_MS);
        int data = 99;                                                    // 发送的数据
        SendState = xQueueSend(QueueHandle, &data, 10 / portTICK_PERIOD_MS);    // 发送队列
        if(SendState != pdPASS)
            ESP_LOGI(TAG,"data send fail");
    }
}
/**
 * @brief APP_Task_Queue_Send_Two
 * @param arg 任务传入的参数
 * @return NULL
 */
void APP_Task_Queue_Send_Two(void *arg)
{
    
    
    QueueHandle_t QueueHandle = (QueueHandle_t)arg;
     BaseType_t SendState;
    while (1)
    {
    
    
        vTaskDelay(2000 / portTICK_PERIOD_MS);
        int data = 168;                                                    // 发送的数据
        SendState = xQueueSend(QueueHandle, &data, 10 / portTICK_PERIOD_MS);    // 发送队列
        if(SendState != pdPASS)
            ESP_LOGI(TAG,"data send fail");
    }
}
/**
 * @brief APP_Task_Queue_Send_Three
 * @param arg 任务传入的参数
 * @return NULL
 */
void APP_Task_Queue_Send_Three(void *arg)
{
    
    
    QueueHandle_t QueueHandle = (QueueHandle_t)arg;
     BaseType_t SendState;
    while (1)
    {
    
    
        vTaskDelay(2000 / portTICK_PERIOD_MS);
        int data = 1688;                                                    // 发送的数据
        SendState = xQueueSend(QueueHandle, &data, 10 / portTICK_PERIOD_MS);    // 发送队列
        if(SendState != pdPASS)
            ESP_LOGI(TAG,"data send fail");
    }
}
/**
 * @brief   APP_Task_Queue_Get
 * @param   NULL
 * @return  NULL
 */
void APP_Task_Queue_Get(void *arg)
{
    
    
    QueueHandle_t QueueHandle = (QueueHandle_t)arg;
    BaseType_t SendState;
    int data = 0;
    while (1)
    {
    
      
        SendState = xQueueReceive(QueueHandle, &data, portMAX_DELAY); // 获取接受状态        
        if(SendState != pdPASS)                                                 // 接受状态
            ESP_LOGI(TAG,"data send fail");
        else
            ESP_LOGI(TAG, "Int type Queue Receive DATA:%d", data);
    }
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void TaskQueue_More_Input_One_Output_Init(void)
{
    
    
    TaskHandle_t APP_Task_Queue_Handle = NULL;
    APP_Task_Queue_Handle = xQueueCreate(6, sizeof(int));
    if (APP_Task_Queue_Handle != NULL)
    {
    
    
        // 创建任务一
        xTaskCreate(APP_Task_Queue_Send_One,    // 创建任务
                    "Task_Queue_Send1",         // 创建任务名
                    2048,                       // 任务堆栈
                    (void *)APP_Task_Queue_Handle,
                    1,     // 默认1
                    NULL); // 任务句柄
        // 创建任务二
        xTaskCreate(APP_Task_Queue_Send_Two,    // 创建任务
                    "Task_Queue_Send2",         // 创建任务名
                    2048,                       // 任务堆栈
                    (void *)APP_Task_Queue_Handle,
                    2,     // 默认2
                    NULL); // 任务句柄
        // 创建任务三
        xTaskCreate(APP_Task_Queue_Send_Three,  // 创建任务
                    "Task_Queue_Send3",         // 创建任务名
                    2048,                       // 任务堆栈
                    (void *)APP_Task_Queue_Handle,
                    3,     // 默认3
                    NULL); // 任务句柄
        // 创建任务四
        xTaskCreate(APP_Task_Queue_Get, // 创建任务
                    "Task_Queue_Get",   // 创建任务名
                    2048,               // 任务堆栈
                    (void *)APP_Task_Queue_Handle,
                    4,     // 默认4
                    NULL); // 任务句柄
        ESP_LOGI(TAG, "Task create ok");
    }
    else
    {
    
    
        ESP_LOGI(TAG, "Task create fail");
    }
    vTaskDelay(3000 / portTICK_PERIOD_MS); // 延时等待
}

队列的邮箱

/**
 * @file 10_TaskQueue_Mail.c
 * @author WSP
 * @brief 队列的邮箱 一个队列发送多个队列接受但是不会清空发送队列中的数值,只有发送队列才可以清空或者覆写队列邮箱的数值
 * @version 0.1
 * @date 2022-10-18
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_TaskQueue_Mail";

/**
 * @brief APP_Task_Queue_Send
 * @param arg 任务传入的参数
 * @return NULL
 */
void APP_Task_Queue_Send(void *arg)
{
    
    
    QueueHandle_t QueueMailbox = (QueueHandle_t)arg;
    BaseType_t SendState;
    int data = 99;                                      // 发送的数据
    while (1)
    {
    
    
        vTaskDelay(3000 / portTICK_PERIOD_MS);
        SendState = xQueueOverwrite(QueueMailbox, &data);   // 发送队列
        if(SendState != pdPASS)
            ESP_LOGI(TAG,"data send fail");
        data ++;
        if(data >= 168)
            data = 0;
    }
}
/**
 * @brief   APP_Task_Queue_Get_One
 * @param   NULL
 * @return  NULL
 */
void APP_Task_Queue_Get_One(void *arg)
{
    
    
    QueueHandle_t QueueHandle = (QueueHandle_t)arg;
    BaseType_t SendState;
    int data = 0;
    while (1)
    {
    
                                                            
        SendState = xQueuePeek(QueueHandle, &data, portMAX_DELAY);  // 获取接受状态  
        if(SendState == pdPASS)      
            ESP_LOGI(TAG, "APP_Task_Queue_Get_One:%d", data);  
        vTaskDelay(3000 / portTICK_PERIOD_MS);                      // 延时等待
    }
}
/**
 * @brief   APP_Task_Queue_Get_Two
 * @param   NULL
 * @return  NULL
 */
void APP_Task_Queue_Get_Two(void *arg)
{
    
    
    QueueHandle_t QueueHandle = (QueueHandle_t)arg;
    BaseType_t SendState;
    int data = 0;
    while (1)
    {
    
                                                            
        SendState = xQueuePeek(QueueHandle, &data, portMAX_DELAY);  // 获取接受状态
        if(SendState == pdPASS)      
            ESP_LOGI(TAG, "APP_Task_Queue_Get_Two:%d", data);  
        vTaskDelay(3000 / portTICK_PERIOD_MS);                      // 延时等待
    }
}
/**
 * @brief   APP_Task_Queue_Get_Three
 * @param   NULL
 * @return  NULL
 */
void APP_Task_Queue_Get_Three(void *arg)
{
    
    
    QueueHandle_t QueueHandle = (QueueHandle_t)arg;
    BaseType_t SendState;
    int data = 0;
    while (1)
    {
    
                                                            
        SendState = xQueuePeek(QueueHandle, &data, portMAX_DELAY);  // 获取接受状态
        if(SendState == pdPASS)      
            ESP_LOGI(TAG, "APP_Task_Queue_Get_Three:%d", data);  
        vTaskDelay(3000 / portTICK_PERIOD_MS);                      // 延时等待
    }
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void TaskQueue_Mail_Init(void)
{
    
    
    QueueHandle_t Mailbox_Handle = NULL;
    Mailbox_Handle = xQueueCreate(1, sizeof(int));      // 创建队列一
    if (Mailbox_Handle != NULL)
    {
    
    
        // 创建任务接收任务一
        xTaskCreate(APP_Task_Queue_Send,        // 创建任务
                    "Task_Queue_Send",          // 创建任务名
                    2048,                       // 任务堆栈
                    (void *)Mailbox_Handle,
                    1,                          // 默认1
                    NULL);                      // 任务句柄
        // 创建任务接收任务二
        xTaskCreate(APP_Task_Queue_Get_One,     // 创建任务
                    "Task_Queue_Get1",          // 创建任务名
                    2048,                       // 任务堆栈
                    (void *)Mailbox_Handle,
                    2,                          // 默认2
                    NULL);                      // 任务句柄
        // 创建任务接收任务三
        xTaskCreate(APP_Task_Queue_Get_Three,   // 创建任务
                    "Task_Queue_Get3",          // 创建任务名
                    2048,                       // 任务堆栈
                    (void *)Mailbox_Handle,
                    2,                          // 默认2
                    NULL);                      // 任务句柄
        // 创建任务接收任务一       
        xTaskCreate(APP_Task_Queue_Get_Two,     // 创建任务
                    "Task_Queue_Get2",          // 创建任务名
                    2048,                       // 任务堆栈
                    (void *)Mailbox_Handle,
                    3,                          // 默认4
                    NULL);                      // 任务句柄
        ESP_LOGI(TAG, "Task create ok");
    }
    else
    {
    
    
        ESP_LOGI(TAG, "Task create fail");
    }
    vTaskDelay(3000 / portTICK_PERIOD_MS); // 延时等待
}

二六 、队列集

1、xQueueCreateSet()

API原型:

QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength);

  必须在 FreeRTOSConfig.h 中将 configUSE_QUEUE_SETS 设置为 1,xQueueCreateSet() API 函数才可用。
  队列集提供了一种机制,允许 RTOS 任务从多个 RTOS 队列或信号量同时进行读取操作后阻塞(挂起)。 请注意, 除了使用队列集之外,还有更简单的替代方法。 有关详细信息,请参阅 阻塞多个对象页面 。
  必须使用调用 xQueueCreateSet() 显式创建队列集, 然后才能使用它。 创建后,可以将标准 FreeRTOS 队列和信号量添加到集合中 (通过调用 xQueueAddToSet())。 然后,使用 xQueueSelectFromSet() 确定集合中包含的队列或信号量中的哪些(如果有) 处于队列读取或信号量获取操作将成功的状态。
注意:

  • 队列和信号量在添加到队列集时必须为空 。 在添加对象时要特别小心, 比如使用已经可用的信号量创建的二进制信号量 [如果使用 vSemaphoreCreateBinary() 宏创建信号量,则属于这种情况, 但如果信号量是使用首选的 xSemaphoreCreateBinary() 函数创建的,则不是这种情况]。
  • 阻塞包含互斥锁的队列集不会导致 互斥锁持有者继承已阻塞任务的优先级。
  • 添加到队列集的每个队列中的每个空格都需要额外的 4 个字节 RAM 。 因此,不应将具有较高最大计数值的计数信号量 添加到队列集中。
  • 不得对队列集的成员执行接收(若为队列)或获取(若为 信号量)操作,除非 调用 xQueueSelectFromSet() 先返回了队列集成员的句柄。

参数:
  uxEventQueueLength 队列集存储集合中包含的队列和 信号量上发生的事件。 uxEventQueueLength 指定一次可以排队的最大事件数 。
要绝对确定事件不会丢失, 必须将 uxEventQueueLength 设置为 添加到集合中的队列长度之和,其中二进制信号量和 互斥体长度为 1,计数信号量的 长度由其最大计数值设置。 例如:

  • 如果队列集要保存一个长度为 5 的队列, 另一个长度为 12 的队列和一个二进制信号量, 则 uxEventQueueLength 应设置为 (5 + 12 + 1) 或 18。
  • 如果队列集要容纳三个二进制信号量, 则 uxEventQueueLength 应设置为 (1 + 1 + 1) 或 3。
  • 如果队列集要保持最大计数为 5 的计数信号量 和最大计数为 3 的计数信号量, 则 uxEventQueueLength 应设置为 (5 + 3) 或 8。

返回:
  如果成功创建队列集,则返回所创建队列集的句柄 。 否则返回 NULL。

/* 定义将添加到队列集中的队列的长度。*/
#define QUEUE_LENGTH_1 10
#define QUEUE_LENGTH_2 10

/* 二进制信号量的有效长度为 1。 */
#define BINARY_SEMAPHORE_LENGTH 1

/* 分别定义队列1和队列2要持有的item的大小。
此处使用的值仅用于演示目的。*/
#define ITEM_SIZE_QUEUE_1 sizeof(uint32_t)
#define ITEM_SIZE_QUEUE_2 sizeof(something_else_t)

/* 两个队列和二进制信号量的组合长度
添加到队列集。*/
#define COMBINED_LENGTH ( QUEUE_LENGTH_1 +
                          QUEUE_LENGTH_2 +
                          BINARY_SEMAPHORE_LENGTH )

void vAFunction(无效)
{
    
    
静态 QueueSetHandle_t xQueueSet;
QueueHandle_t xQueue1,xQueue2,xSemaphore;
QueueSetMemberHandle_t xActivatedMember;
uint32_t xReceivedFromQueue1;
something_else_t xReceivedFromQueue2;

    /* 创建足够大的队列集来为每个空间保存一个事件
    要添加到集合中的每个队列和信号量。*/
    xQueueSet = xQueueCreateSet( COMBINED_LENGTH );

    /* 创建将包含在集合中的队列和信号量。*/
    xQueue1 = xQueueCreate( QUEUE_LENGTH_1, ITEM_SIZE_QUEUE_1 );
    xQueue2 = xQueueCreate( QUEUE_LENGTH_2, ITEM_SIZE_QUEUE_2 );

    /* 创建要添加到集合中的信号量。*/
    xSemaphore = xSemaphoreCreateBinary();

    /* 检查所有内容都已创建。*/
    configASSERT( xQueueSet );
    配置断言(xQueue1);
    配置断言(xQueue2);
    configASSERT( xSemaphore );

    /* 将队列和信号量添加到集合中。从这些队列中读取和
    只能在调用 xQueueSelectFromSet() 后执行信号量
    从此时起返回队列或信号量句柄。*/
    xQueueAddToSet( xQueue1, xQueueSet );
    xQueueAddToSet( xQueue2, xQueueSet );
    xQueueAddToSet( xSemaphore, xQueueSet );

    为了( ;;{
    
    
        /* 阻塞以等待队列中可用的东西或
        已添加到集合中的信号量。不要阻塞超过
        200 毫秒。*/
        xActivatedMember = xQueueSelectFromSet( xQueueSet,
                                                200 / 端口TICK_PERIOD_MS );

        /* 选择了哪个集合成员?接收/获取可以使用块时间
        为零,因为它们保证通过,因为 xQueueSelectFromSet()
        除非有可用的东西,否则不会返回句柄。*/
        如果(xActivatedMember == xQueue1)
        {
    
    
            xQueueReceive( xActivatedMember, &xReceivedFromQueue1, 0 );
            vProcessValueFromQueue1( xReceivedFromQueue1 );
        }
        否则如果(xActivatedMember == xQueue2)
        {
    
    
            xQueueReceive( xActivatedMember, &xReceivedFromQueue2, 0 );
            vProcessValueFromQueue2( &xReceivedFromQueue2 );
        }
        否则如果(xActivatedMember == xSemaphore)
        {
    
    
            /* 获取信号量以确保它可以再次“给定”。*/
            xSemaphoreTake( xActivatedMember, 0 );
            vProcessEventNotifiedBySemaphore();
            休息;
        }
        别的
        {
    
    
            /* 200 毫秒的块时间在没有 RTOS 队列或信号量的情况下到期
            准备好处理。*/
        }
    }
}

2、xQueueAddToSet()

API原型:

BaseType_t xQueueAddToSet
                     (
                         QueueSetMemberHandle_t xQueueOrSemaphore,
                         QueueSetHandle_t xQueueSet
                     );

  必须在 FreeRTOSConfig.h 中将 configUSE_QUEUE_SETS 设置为 1,这样 xQueueAddToSet () API 函数才可用。
  将 RTOS 队列或信号量添加至先前由 xQueueCreateSet() 调用创建的队列集。
  接收(如果是队列)或获取(如果是 信号量)操作,除非 调用 xQueueSelectFromSet() 先返回了队列集成员的句柄。
参数:
  xQueueOrSemaphore 正在添加到队列集的队列或信号量的句柄 (转换为 QueueSetMemberHandle_t 类型)。
  xQueueSet 正在添加队列或信号量的队列集句柄 。
返回:
  如果队列或信号量成功添加到队列集 那么返回 pdPASS。 如果队列无法成功添加到 队列集,因为它已经是其他队列集的成员,那么返回 pdFAIL 。
用法示例:
  请参阅 xQueueCreateSet() 文档页面上的示例。

3、xQueueRemoveFromSet()

API原型:

BaseType_t xQueueRemoveFromSet
                     (
                         QueueSetMemberHandle_t xQueueOrSemaphore,
                         QueueSetHandle_t xQueueSet
                     );

  必须在 FreeRTOSConfig.h 中将 configUSE_QUEUE_SETS 设置为 1,xQueueRemoveFromSet() API 函数才可用。
  从队列集中删除 RTOS 队列或信号量。
  仅当队列或信号量为空时,才能从队列集中删除 RTOS 队列或信号量 。
参数:
  xQueueOrSemaphore 从队列集中删除的队列或信号量的句柄 (转换为 QueueSetMemberHandle_t 类型)。
  xQueueSet 包含队列或信号量的队列集的句柄 。
返回:
  如果队列或信号量已成功从队列集中删除, 则返回 pdPASS。 如果队列不在队列集中,或者 队列(或信号量)不为空,则返回 pdFAIL。
用法示例:
  此示例假定 xQueueSet 是已创建的队列集, 而 xQueue 是已创建并添加到 xQueueSet 中的队列。

4、xQueueSelectFromSet()

API原型:

QueueSetMemberHandle_t xQueueSelectFromSet
                      (
                            QueueSetHandle_t xQueueSet,
                            const TickType_t xTicksToWait
                       );

  必须在 FreeRTOSConfig.h 中将 configUSE_QUEUE_SETS 设置为 1,才能使用 xQueueSelectFromSet() API 函数。
  xQueueSelectFromSet() 从队列集成员中选择队列或信号量, 它们要么包含数据(若选择队列),要么可供获取 (若选择信号量)。 xQueueSelectFromSet() 能有效 允许任务同时读取一个队列集中的所有 队列和信号量后阻塞(挂起)。
注意:
  您也可以不使用队列集,而是采用更简单的替代方案。 请参阅 阻塞多个对象页面 了解详情。
  阻塞包含互斥锁的队列不会导致 互斥锁持有者继承被阻塞任务的优先级。
除非 xQueueSelectFromSet() 信号量)操作,除非 调用 xQueueSelectFromSet() 先返回了队列集成员的句柄。
参数:
  xQueueSet 任务(可能)阻塞的队列集。
  xTicksToWait 调用任务保持阻塞状态(其他任务正在执行), 等待队列集成员做好准备 以便成功读取队列或获取信号量所需的最长时间, 以滴答为单位。
返回:
  xQueueSelectFromSet() 将返回 队列集中包含数据的队列的句柄(转换为 QueueSetMemberHandle_t 类型) 或队列集中可用信号量的句柄(转换为 QueueSetMemberHandle_t 类型), 如果在指定的阻塞时间到期之前不存在这样的队列或信号量, 则返回 NULL。

5、使用示例

/**
 * @file 8_TaskQueue_Set.c
 * @author WSP
 * @brief 队列集合队列
 * @version 0.1
 * @date 2022-10-18
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_TaskQueue_Set";

TaskHandle_t APP_Task_Queue_Send_One_Handle = NULL,APP_Task_Queue_Send_Two_Handle = NULL;

/**
 * @brief APP_Task_Queue_Send_One
 * @param arg 任务传入的参数
 * @return NULL
 */
void APP_Task_Queue_Send_One(void *arg)
{
    
    
    QueueHandle_t QueueHandle = (QueueHandle_t)arg;
    BaseType_t SendState;
    while (1)
    {
    
    
        vTaskDelay(2000 / portTICK_PERIOD_MS);
        int data = 99;                                                          // 发送的数据
        SendState = xQueueSend(QueueHandle, &data, 10 / portTICK_PERIOD_MS);    // 发送队列
        if(SendState != pdPASS)
            ESP_LOGI(TAG,"data send fail");
    }
}
/**
 * @brief APP_Task_Queue_Send_Two
 * @param arg 任务传入的参数
 * @return NULL
 */
void APP_Task_Queue_Send_Two(void *arg)
{
    
    
    QueueHandle_t QueueHandle = (QueueHandle_t)arg;
    BaseType_t SendState;
    while (1)
    {
    
    
        vTaskDelay(2000 / portTICK_PERIOD_MS);
        char * data = (char *) malloc(25);
        snprintf(data,25,"My name is WSP");                                 // 发送的数据
        SendState = xQueueSend(QueueHandle, &data, 10 / portTICK_PERIOD_MS);// 发送队列
        if(SendState != pdPASS)
            ESP_LOGI(TAG,"data send fail");
    }
}
/**
 * @brief   APP_Task_Queue_Get
 * @param   NULL
 * @return  NULL
 */
void APP_Task_Queue_Get(void *arg)
{
    
    
    QueueSetHandle_t QueueSetHandle = (QueueSetHandle_t)arg;
    QueueSetMemberHandle_t QueueSetMemberHandle;
    BaseType_t SendState;
    while (1)
    {
    
      
        QueueSetMemberHandle = xQueueSelectFromSet(QueueSetHandle,portMAX_DELAY);   // 获取队列集合
        if(QueueSetMemberHandle == APP_Task_Queue_Send_One_Handle){
    
                     // 对队列集合的句柄判断
            int data = 0;                                                           // 设置接受变量
            SendState = xQueueReceive(QueueSetMemberHandle, &data, portMAX_DELAY);  // 获取接受状态  
            if(SendState == pdPASS)      
                ESP_LOGI(TAG, "APP_Task_Queue_Send_One_Handle Send DATA:%d", data);  
        }else if(QueueSetMemberHandle == APP_Task_Queue_Send_Two_Handle){
    
               // 对队列集合的句柄判断
            char * Pstr = NULL;                                                     // 设置接受变量
            SendState = xQueueReceive(QueueSetMemberHandle, &Pstr, portMAX_DELAY);  // 获取接受状态   
            if(SendState == pdPASS)      
                ESP_LOGI(TAG, "APP_Task_Queue_Send_Two_Handle Send DATA:%s", Pstr);  
        }    
    }
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void TaskQueue_Set_Init(void)
{
    
    
    APP_Task_Queue_Send_One_Handle = xQueueCreate(6, sizeof(int));      // 创建队列一
    APP_Task_Queue_Send_Two_Handle = xQueueCreate(6, sizeof(char *));   // 创建队列二
    QueueSetHandle_t QueueSetHandle = NULL;                             // 创建队列集合句柄
    QueueSetHandle = xQueueCreateSet(6 + 6);                            // 创建队列集合 长度为 队列一 + 队列二
    xQueueAddToSet(APP_Task_Queue_Send_One_Handle,QueueSetHandle);      // 将队列加入队列集合
    xQueueAddToSet(APP_Task_Queue_Send_Two_Handle,QueueSetHandle);      // 将队列加入队列集合
    if (APP_Task_Queue_Send_One_Handle != NULL && APP_Task_Queue_Send_Two_Handle != NULL && QueueSetHandle != NULL)
    {
    
    
        // 创建任务发送任务一
        xTaskCreate(APP_Task_Queue_Send_One,    // 创建任务
                    "Task_Queue_Send1",         // 创建任务名
                    2048,                       // 任务堆栈
                    (void *)APP_Task_Queue_Send_One_Handle,
                    1,                          // 默认1
                    NULL);                      // 任务句柄
        // 创建任务发送任务二
        xTaskCreate(APP_Task_Queue_Send_Two,    // 创建任务
                    "Task_Queue_Send2",         // 创建任务名
                    2048,                       // 任务堆栈
                    (void *)APP_Task_Queue_Send_Two_Handle,
                    2,                          // 默认2
                    NULL);                      // 任务句柄
        // 创建任务接收任务一       
        xTaskCreate(APP_Task_Queue_Get,         // 创建任务
                    "Task_Queue_Get",           // 创建任务名
                    2048,                       // 任务堆栈
                    (void *)QueueSetHandle,
                    4,                          // 默认4
                    NULL);                      // 任务句柄
        ESP_LOGI(TAG, "Task create ok");
    }
    else
    {
    
    
        ESP_LOGI(TAG, "Task create fail");
    }
    vTaskDelay(3000 / portTICK_PERIOD_MS); // 延时等待
}

猜你喜欢

转载自blog.csdn.net/believe666/article/details/127205558
今日推荐