提示:好记性不如烂笔头。本博客作为学习笔记,有错误的地方希望指正
文章目录
- 前言:
- 一、xQueueCreate()
- 二、xQueueCreateStatic()
- 三、vQueueDelete()
- 四、xQueueSend()
- 五、xQueueSendFromISR()
- 六、xQueueSendToBack()
- 七、xQueueSendToBackFromISR()
- 八、xQueueSendToFront()
- 九、xQueueSendToFrontFromISR()
- 十、xQueueReceive()
- 十一、xQueueReceiveFromISR()
- 十二、uxQueueMessagesWaiting()
- 十三、uxQueueMessagesWaitingFromISR()
- 十四、uxQueueSpacesAvailable()
- 十五、xQueueReset()
- 十六、xQueueOverwrite()
- 十七、xQueueOverwriteFromISR()
- 十八、xQueuePeek()
- 十九、xQueuePeekFromISR()
- 二十、vQueueAddToRegistry()
- 二一、vQueueUnregisterQueue()
- 二二、pcQueueGetName()
- 二 三、xQueueIsQueueFullFromISR()
- 二四 、xQueueIsQueueEmptyFromISR()
- 二五 、队列综合示例代码
- 二六 、队列集
前言:
参考资料: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 内核感知调试相关:
- 可以关联文本名称和队列,便于在调试 GUI 中识别队列。
- 包含调试器定位每个已注册队列和信号量所需的信息。
除非使用 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); // 延时等待
}