【FreeRTOS 教程 四】队列创建与发布项目到队列

目录

一、FreeRTOS队列:

(1)队列介绍:

(2)用户模型说明:

(3)阻塞队列:

二、队列管理 API:

(1)uxQueueMessagesWaiting:

(2) uxQueueMessagesWaitingFromISR:

(3)uxQueueSpacesAvailable:

(4)vQueueDelete:

(5)xQueueReset:

(6)xQueueIsQueueEmptyFromISR:

(7)xQueueIsQueueFullFromISR:

三、队列的创建及使用:

(1)动态创建队列:

(2)静态创建队列:

(3)向队列中发布项目:

(4)项目发布到队列尾部:

(5)队列尾部入队数据项:

(6)项目发布到队列尾部:

(7)队列头部入队数据项:

(8)项目发布到队列头部:

四、队列示例程序:

(1)动态创建队列:

(2)静态创建队列:

​五、队列创建时的常见问题:

(1)函数未定义:

​(2)动态堆空间太大导致静态分配空间不足:

(3)队列创建后程序下载没反应:

六、FreeRTOS教程示例代码下载:


一、FreeRTOS队列:

(1)队列介绍:

队列是任务间通信的主要形式。它们可以用于在任务之间以及中断和任务之间发送消息。在大多数情况下,队列用作线程安全的 FIFO(先进先出)缓冲区, 新数据被发送到队列的后面,但也可以发送到前面。

向队列中写入和从队列中读取。此示例中创建队列来保存 5 个项目,并且队列永远不会满。

(2)用户模型说明:

FreeRTOS 队列使用模型既简单又灵活, 这两者通常是不可兼得的。消息通过队列以副本的方式发送, 这意味着数据(可以是更大的缓冲区的指针)本身被复制到队列中, 而不是队列始终只存储对数据的引用。这是最好的方法,因为:

  • 已经包含在 C 语言变量(整数、 小结构体等)中的小消息可以直接送入队列。没有 必要为消息分配一个缓冲区, 然后将变量复制到分配的缓冲区中。同样,可以直接从队列中将消息读取到 C 变量中 。
  • 此外,以这种方式向队列发送消息, 允许发送任务立即覆盖发送到队列的变量或缓冲区, 即使发送的消息仍在队列中。
  • 由于变量中包含的数据已复制到队列中, 变量本身可以重复使用。不要求发送消息的任务 和接收消息的任务约定哪个任务拥有该消息, 以及哪个任务负责在不需要该消息时 将其清空。
  • 使用通过复制传递数据的队列不会导致无法将队列 用于通过引用传递数据。当消息的大小达到一定程度, 将整条消息逐字节复制到队列中是不现实的, 此时可将消息定义为保存若干指针并复制消息的 一个指针至队列。
  • 内核独自负责分配用于队列存储区的内存 。
  • 可变大小的消息可以通过定义队列来保存结构体, 其中包含一个指向队列消息的成员, 以及另一个保存队列消息大小的成员。
  • 单个队列可用于接收不同的消息类型, 以及来自多个地点的消息, 方法是将队列定义为保存一个结构体,该结构的一个成员持有消息类型, 另一个成员保存消息数据(或消息数据的一个指针)。如何解释数据 取决于消息类型。
  • 正是使用这种方式,管理 FreeRTOS-Plus-TCP IP 堆栈的任务才能使用一个队列来接收 ARP 定时器事件、 从以太网硬件接收的数据包、 从应用程序接收的数据包、网络故障事件等的通知。
  • 该实现适用于在内存保护环境中使用 。一个被限制在受保护的内存区域的任务可以将数据传递给一个被限制在不同的受保护内存区域的任务, 因为通过调用队列发送函数 来调用 RTOS 将提高微控制器的权限等级 。队列存储区 仅可由 RTOS 访问(具有完整权限)。
  • 提供一个单独的 API 用于中断内部。将 RTOS 任务中使用的 API 与中断服务程序中使用的 API 分开, 意味着 RTOS API 函数的实现 不承担每次执行时检查其调用上下文的开销。 使用单独的中断 API 也意味着,在大多数情况下,创建 RTOS 感知的中断服务程序对终端用户而言更简单—— 与其他 RTOS 产品相比。
  • 从任何意义上来说,API 都更加简单。

(3)阻塞队列:

  • 队列 API 函数允许指定阻塞时间。
  • 当一个任务试图从一个空队列中读取时,该队列将进入阻塞状态(因此它不会消耗任何 CPU 时间,且其他任务可以运行) 直到队列中的数据变得可用,或者阻塞时间过期。
  • 当一个任务试图写入到一个满队列时,该队列将进入阻塞状态(因此它不会消耗任何 CPU 时间,且其他任务可以运行) 直到队列中出现可用空间,或者阻塞时间过期。
  • 如果同一个队列上有多个处于阻塞状态的任务, 那么具有最高优先级的任务将最先解除阻塞。
  • 请注意,中断只能使用以 "FromISR" 结尾的 API 函数。

二、队列管理 API:

(1)uxQueueMessagesWaiting

  • 作用:返回队列中存储的消息数。

函数原型:

UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue );
参数/返回描述
xQueue正在查询的队列的句柄。
返回值队列中可用的消息数。

(2) uxQueueMessagesWaitingFromISR

  • 作用:uxQueueMessagesWaiting()的一个版本,可以从 ISR 中调用。返回队列中存储的消息数。

函数原型:

UBaseType_t uxQueueMessagesWaitingFromISR( QueueHandle_t xQueue );
参数/返回描述
xQueue正在查询的队列的句柄。
返回值队列中可用的消息数。

(3)uxQueueSpacesAvailable

  • 作用:返回队列中的可用空间数。

函数原型:

UBaseType_t uxQueueSpacesAvailable( QueueHandle_t xQueue );
参数/返回值描述
xQueue正在查询的队列的句柄。
返回值队列中可用的可用空间数。

(4)vQueueDelete

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

函数原型:

void vQueueDelete( QueueHandle_t xQueue );
参数描述
xQueue要删除的队列的句柄。

(5)xQueueReset

  • 作用:将队列重置为其原始的空状态。

函数原型:

BaseType_t xQueueReset( QueueHandle_t xQueue );
参数/返回值描述
xQueue正在重置的队列的句柄。
返回值因为 FreeRTOS V7.2.0 中 xQueueReset() 总是返回 pdPASS

(6)xQueueIsQueueEmptyFromISR

  • 作用:查询队列以确定队列是否为空。此函数只能用于 ISR。 

函数原型:

BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t pxQueue );
参数/返回值描述
xQueue正在查询的队列的句柄。
返回值如果队列不为空,则返回 pdFALSE;如果队列为空,则返回 pdTRUE

(7)xQueueIsQueueFullFromISR

  • 作用:查询队列以确定队列是否已满。此函数只能用于 ISR。

函数原型:

BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t pxQueue );
参数/返回描述
xQueue正在查询的队列的句柄。
返回值如果队列未满,则返回 pdFALSE;如果队列已满,则返回 pdTRUE

三、队列的创建及使用:

(1)动态创建队列:

函数原型:

 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。

用法示例:

struct AMessage
{char ucMessageID;        // 消息ID,使用1个字节的字符类型存储char ucData[20];         // 数据字段,使用20个字节的字符数组存储
};void vATask( void *pvParameters )
{QueueHandle_t xQueue1, xQueue2; // 定义两个队列句柄变量/* 创建一个能够存储10个无符号长整型(unsigned long)的队列 */xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );if( xQueue1 == NULL ) // 检查队列是否创建成功{/* 如果队列创建失败(内存分配失败),则不能使用这个队列 */}/* 创建一个能够存储10个指向AMessage结构体的指针的队列这些结构体通过指针入队,因为它们相对较大 */xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );if( xQueue2 == NULL ) // 检查队列是否创建成功{/* 如果队列创建失败(内存分配失败),则不能使用这个队列 */}/* ... 任务代码的其余部分 */
}

(2)静态创建队列:

函数原型:

 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。

用法示例:

/* 定义队列的最大长度为10,即队列可以存储10个uint64_t类型的变量。 */
#define QUEUE_LENGTH    10/* 定义每个队列项的大小为uint64_t类型的大小。 */
#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值的静态队列。xQueueCreateStatic函数的参数分别为:- QUEUE_LENGTH:队列的最大长度。- ITEM_SIZE:每个队列项的大小。- ucQueueStorageArea:指向队列存储区域的指针。- &xStaticQueue:指向用于保存队列数据结构的静态变量的指针。 */xQueue = xQueueCreateStatic( QUEUE_LENGTH,ITEM_SIZE,ucQueueStorageArea,&xStaticQueue );/* 由于 pxQueueBuffer(即 &xStaticQueue)不为 NULL,因此 xQueue 不应为 NULL。使用 configASSERT 宏来断言 xQueue 不为 NULL,如果 xQueue 为 NULL,则触发断言失败。 */configASSERT( xQueue );
}

(3)向队列中发布项目:

函数原型:

 BaseType_t xQueueSend(QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t xTicksToWait);
  • 此宏用于调用 xQueueGenericSend() 函数。之所以包含此宏,是为了向后兼容那些未提供 xQueueSendToFront()xQueueSendToBack() 宏的 FreeRTOS 版本。其功能等同于 xQueueSendToBack()
  • 在队列中发布项目。项目通过复制而非引用的方式入队。不得从中断服务程序中调用此函数。请参阅 xQueueSendFromISR(),这是一个可在 ISR 中使用的替代函数。
参数/返回值描述
xQueue要向其中发布项目的队列的句柄。
pvItemToQueue指向要放入队列中的项目的指针。队列能够存储的项目的大小在创建队列时即已定义,因此 pvItemToQueue 中的这些字节将复制到队列存储区中。
xTicksToWait队列已满的情况下,任务处于阻塞状态且愿意等待队列中出现可用空间的最长时间。如果队列已满且 xTicksToWait 设置为 0,则调用将立即返回。时间以滴答周期为单位定义,如果需要转换为实际时间,可以使用 portTICK_PERIOD_MS 常量。如果 INCLUDE_vTaskSuspend 设置为 1,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时限制)。
返回如果成功发布项目,返回 pdTRUE,否则返回 errQUEUE_FULL

 用法示例:

struct AMessage
{char ucMessageID;        // 消息ID,使用1个字节的字符类型存储char ucData[20];         // 数据字段,使用20个字节的字符数组存储
} xMessage;                // 定义一个AMessage类型的变量xMessageunsigned long ulVar = 10UL; // 定义一个无符号长整型变量ulVar并初始化为10void vATask(void *pvParameters)
{QueueHandle_t xQueue1, xQueue2; // 定义两个队列句柄变量struct AMessage *pxMessage;    // 定义一个指向AMessage结构体的指针变量/* 创建一个能够存储10个无符号长整型(unsigned long)的队列 */xQueue1 = xQueueCreate(10, sizeof(unsigned long));/* 创建一个能够存储10个指向AMessage结构体的指针的队列这些结构体通过指针入队,因为它们相对较大 */xQueue2 = xQueueCreate(10, sizeof(struct AMessage*));/* ... 其他任务代码 ... */if (xQueue1 != NULL) // 检查xQueue1是否创建成功{/* 向xQueue1发送一个无符号长整型变量。如果队列满,最多等待10个滴答周期 */if (xQueueSend(xQueue1, (void*)&ulVar, (TickType_t)10) != pdPASS){/* 发送失败,即使等待了10个滴答周期 */}}if (xQueue2 != NULL) // 检查xQueue2是否创建成功{/* 向xQueue2发送一个指向AMessage结构体的指针。如果队列满,不等待 */pxMessage = &xMessage; // 将pxMessage指向xMessagexQueueSend(xQueue2, (void*)&pxMessage, (TickType_t)0);}/* ... 任务代码的其余部分 */
}

(4)项目发布到队列尾部:

函数原型:

 BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);
  • 此宏用于调用 xQueueGenericSendFromISR() 函数。包含此宏是为了向后兼容某些版本的 FreeRTOS,这些版本未提供 xQueueSendToBackFromISR()xQueueSendToFrontFromISR() 宏。
  • 将项目发布到队列尾部。可以在中断服务程序中安全使用此函数。
  • 项目通过复制而非引用的方式入队,因此最好只将较小的项目放入队列,特别是从 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(); // 如果有更高优先级的任务被唤醒,则请求上下文切换}
}

(5)队列尾部入队数据项:

函数原型:

 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;        // 消息ID,使用1个字节的字符类型存储char ucData[20];         // 数据字段,使用20个字节的字符数组存储
} xMessage;                // 定义一个AMessage类型的变量xMessageunsigned long ulVar = 10UL; // 定义一个无符号长整型变量ulVar并初始化为10void vATask(void *pvParameters)
{QueueHandle_t xQueue1, xQueue2; // 定义两个队列句柄变量struct AMessage *pxMessage;    // 定义一个指向AMessage结构体的指针变量/* 创建一个能够存储10个无符号长整型(unsigned long)的队列 */xQueue1 = xQueueCreate(10, sizeof(unsigned long));/* 创建一个能够存储10个指向AMessage结构体的指针的队列这些结构体通过指针入队,因为它们相对较大 */xQueue2 = xQueueCreate(10, sizeof(struct AMessage*));/* ... 其他任务代码 ... */if (xQueue1 != NULL) // 检查xQueue1是否创建成功{/* 向xQueue1发送一个无符号长整型变量。如果队列满,最多等待10个滴答周期 */if (xQueueSendToBack(xQueue1, (void*)&ulVar, (TickType_t)10) != pdPASS){/* 发送失败,即使等待了10个滴答周期 */}}if (xQueue2 != NULL) // 检查xQueue2是否创建成功{/* 向xQueue2发送一个指向AMessage结构体的指针。如果队列满,不等待 */pxMessage = &xMessage; // 将pxMessage指向xMessagexQueueSendToBack(xQueue2, (void*)&pxMessage, (TickType_t)0);}/* ... 任务代码的其余部分 */
}

(6)项目发布到队列尾部:

函数原型:

 BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);
  • 此宏用于调用 xQueueGenericSendFromISR() 函数。
  • 将项目发布到队列尾部。可以在中断服务程序中安全使用此函数。
  • 项目通过复制而非引用的方式入队,因此最好只将较小的项目放入队列,特别是从 ISR 调用时。
参数/返回值描述
xQueue要向其中发布项目的队列的句柄。
pvItemToQueue指向要放入队列中的项目的指针。队列能够存储的项目的大小在创建队列时即已定义,因此 pvItemToQueue 中的这些字节将复制到队列存储区中。
pxHigherPriorityTaskWoken如果发送到队列会导致任务解除阻塞,并且解除阻塞的任务的优先级高于当前正在运行的任务,则 xQueueSendToBackFromISR() 会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。如果 xQueueSendToBackFromISR() 将此值设置为 pdTRUE,则应在中断退出前请求上下文切换。从 FreeRTOS V7.3.0 开始,pxHigherPriorityTaskWoken 为可选参数,可设置为 NULL。
返回如果成功发送至队列,则返回 pdPASS,否则返回 errQUEUE_FULL

用法示例:

缓冲 IO 的用法示例(每次调用时 ISR 可获得多个值):

void vBufferISR(void)
{char cIn; // 定义一个字符变量cIn,用于临时存储从接收寄存器读取的字节BaseType_t xHigherPriorityTaskWoken; // 定义一个BaseType_t类型的变量,用于指示是否有更高优先级的任务被唤醒/* 在ISR开始时,我们还没有唤醒任何任务。 */xHigherPriorityTaskWoken = pdFALSE; // 初始化xHigherPriorityTaskWoken为pdFALSE/* 循环直到缓冲区为空。 */do{/* 从指定的接收寄存器地址RX_REGISTER_ADDRESS读取一个字节。 */cIn = portINPUT_BYTE(RX_REGISTER_ADDRESS); // 从接收寄存器读取一个字节到cIn/* 将读取的字节发送到队列xRxQueue的尾部。 */xQueueSendToBackFromISR(xRxQueue, &cIn, &xHigherPriorityTaskWoken); // 将cIn发送到队列,如果操作导致更高优先级任务被唤醒,xHigherPriorityTaskWoken会被设置为pdTRUE} while(portINPUT_BYTE(BUFFER_COUNT)); // 检查缓冲区计数器,如果非零则继续循环/* 现在缓冲区为空,如果有必要,我们可以切换上下文。 */if(xHigherPriorityTaskWoken) // 如果有更高优先级的任务被唤醒{/* 使用特定于端口的宏请求上下文切换。 */taskYIELD_FROM_ISR(); // 请求上下文切换,以运行更高优先级的任务}
}

(7)队列头部入队数据项:

函数原型:

 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;        // 消息ID,使用1个字节的字符类型存储char ucData[20];         // 数据字段,使用20个字节的字符数组存储
} xMessage;                // 定义一个AMessage类型的变量xMessageunsigned long ulVar = 10UL; // 定义一个无符号长整型变量ulVar并初始化为10void vATask(void *pvParameters)
{QueueHandle_t xQueue1, xQueue2; // 定义两个队列句柄变量struct AMessage *pxMessage;    // 定义一个指向AMessage结构体的指针变量/* 创建一个能够存储10个无符号长整型(unsigned long)的队列 */xQueue1 = xQueueCreate(10, sizeof(unsigned long));/* 创建一个能够存储10个指向AMessage结构体的指针的队列这些结构体通过指针入队,因为它们相对较大 */xQueue2 = xQueueCreate(10, sizeof(struct AMessage*));/* ... 其他任务代码 ... */if (xQueue1 != NULL) // 检查xQueue1是否创建成功{/* 向xQueue1发送一个无符号长整型变量。如果队列满,最多等待10个滴答周期 */if (xQueueSendToFront(xQueue1, (void*)&ulVar, (TickType_t)10) != pdPASS){/* 发送失败,即使等待了10个滴答周期 */}}if (xQueue2 != NULL) // 检查xQueue2是否创建成功{/* 向xQueue2发送一个指向AMessage结构体的指针。如果队列满,不等待 */pxMessage = &xMessage; // 将pxMessage指向xMessagexQueueSendToFront(xQueue2, (void*)&pxMessage, (TickType_t)0);}/* ... 任务代码的其余部分 */
}

(8)项目发布到队列头部:

函数原型:

 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

用法示例:

void vBufferISR(void)
{char cIn; // 定义一个字符变量cIn,用于临时存储从接收寄存器读取的字节BaseType_t xHigherPriorityTaskWoken; // 定义一个BaseType_t类型的变量,用于指示是否有更高优先级的任务被唤醒/* 在ISR开始时,我们还没有唤醒任何任务。 */xHigherPriorityTaskWoken = pdFALSE; // 初始化xHigherPriorityTaskWoken为pdFALSE/* 从指定的接收寄存器地址RX_REGISTER_ADDRESS读取一个字节。 */cIn = portINPUT_BYTE(RX_REGISTER_ADDRESS); // 从接收寄存器读取一个字节到cInif (cIn == EMERGENCY_MESSAGE) // 检查读取的字节是否为紧急消息{/* 如果是紧急消息,将字节发送到队列的前面。 */xQueueSendToFrontFromISR(xRxQueue, &cIn, &xHigherPriorityTaskWoken); // 将cIn发送到队列的前面,如果操作导致更高优先级任务被唤醒,xHigherPriorityTaskWoken会被设置为pdTRUE}else // 如果不是紧急消息{/* 将字节发送到队列的后面。 */xQueueSendToBackFromISR(xRxQueue, &cIn, &xHigherPriorityTaskWoken); // 将cIn发送到队列的后面,如果操作导致更高优先级任务被唤醒,xHigherPriorityTaskWoken会被设置为pdTRUE}/* 检查是否发送到队列导致更高优先级的任务被唤醒。 */if (xHigherPriorityTaskWoken) // 如果有更高优先级的任务被唤醒{/* 使用特定于端口的宏请求上下文切换。 */taskYIELD_FROM_ISR(); // 请求上下文切换,以运行更高优先级的任务}
}

四、队列示例程序:

(1)动态创建队列:

动态创建两个任务,一个用于发送消息到队列,另一个用于从队列接收消息并通过串口打印。

#include "stm32f10x.h"                  // 包含STM32F10x系列微控制器的头文件
#include "FreeRTOS.h"
#include "task.h"                      // 包含任务相关函数的头文件,用于任务创建和管理。
#include "queue.h"
#include "stdio.h"
#include "uart.h"/***********************************
* @method  			创建两个任务,一个用于发送消息到队列,
*			 			另一个用于从队列接收消息并通过串口打印。
* @Platform  		CSDN
* @author  			The_xzs
* @date    			2025.1.25
************************************/
// 定义一个消息结构体,用于存储消息ID和消息数据
struct AMessage
{char ucMessageID;        // 消息ID,使用1个字节的字符类型存储char ucData[20];         // 数据字段,使用20个字节的字符数组存储
};QueueHandle_t xQueue1, xQueue2; // 定义两个队列句柄变量//发送任务函数
void vSenderTask(void *pvParameters){unsigned long ulValueToSend = 0; 				// 用于发送的无符号长整型变量struct AMessage *pxMessage;     				// 指向AMessage结构体的指针for( ;; ){ulValueToSend++; 							// 每次循环递增发送值pxMessage = (struct AMessage *) pvPortMalloc(sizeof(struct AMessage)); // 动态分配内存if(pxMessage != NULL) 								// 检查内存分配是否成功{pxMessage->ucMessageID = (char)ulValueToSend; 	// 设置消息IDsnprintf(pxMessage->ucData, 20, "Message %lu", ulValueToSend); // 格式化消息数据// 将无符号长整型值发送到队列xQueue1if( xQueueSend( xQueue1, &ulValueToSend, portMAX_DELAY ) != pdPASS ){// 如果发送失败,添加错误处理代码printf("xQueue1 failed to send\r\n");}// 将AMessage结构体的指针发送到队列xQueue2if( xQueueSend( xQueue2, &pxMessage, portMAX_DELAY ) != pdPASS ){printf("xQueue2 failed to send\r\n");}}vTaskDelay(1000 / portTICK_RATE_MS); // 延时1秒}
}// 接收任务函数
void vReceiverTask( void *pvParameters )
{unsigned long ulReceivedValue; // 用于接收的无符号长整型变量struct AMessage *pxReceivedMessage; // 指向AMessage结构体的指针for( ;; ) // 无限循环,任务持续运行{// 从队列xQueue1接收数据if( xQueueReceive( xQueue1, &ulReceivedValue, portMAX_DELAY ) == pdPASS ){// 如果接收成功,通过串口打印接收到的值printf("Received from xQueue1: %lu\r\n", ulReceivedValue);}// 从队列xQueue2接收数据if( xQueueReceive( xQueue2, &pxReceivedMessage, portMAX_DELAY ) == pdPASS ){// 如果接收成功,通过串口打印接收到的消息ID和数据printf("Received from xQueue2: ID = %d, Data = %s\r\n", pxReceivedMessage->ucMessageID, pxReceivedMessage->ucData);vPortFree(pxReceivedMessage); // 释放动态分配的内存}}
}// 主函数
int main(void)
{Uart_Init(115200); DMA1_Init();      /* 创建一个能够存储10个无符号长整型(unsigned long)的队列 */xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );if( xQueue1 == NULL ) // 检查队列是否创建成功{/* 如果队列创建失败(内存分配失败),则不能使用这个队列 */while(1); // 无限循环,停止程序运行}/* 创建一个能够存储10个指向AMessage结构体的指针的队列这些结构体通过指针入队,因为它们相对较大 */xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );if( xQueue2 == NULL ) // 检查队列是否创建成功{/* 如果队列创建失败(内存分配失败),则不能使用这个队列 */while(1); // 无限循环,停止程序运行}/* 创建发送和接收任务 */xTaskCreate( vSenderTask, "Sender", 128, NULL, 2, NULL ); 		// 创建发送任务xTaskCreate( vReceiverTask, "Receiver", 128, NULL, 2, NULL ); 	// 创建接收任务// 启动任务调度器,FreeRTOS开始执行任务的地方vTaskStartScheduler();  return 0;  // 理论上不会到达这里
}

效果:

(2)静态创建队列:

静态创建两个任务,一个用于发送消息到队列,另一个用于从队列接收消息并通过串口打印。

#include "stm32f10x.h"                  // 包含STM32F10x系列微控制器的头文件
#include "FreeRTOS.h"
#include "task.h"                      // 包含任务相关函数的头文件,用于任务创建和管理。
#include "queue.h"
#include "stdio.h"
#include "uart.h"/***********************************
* @method  			静态创建两个任务,一个用于发送消息到队列,
*			 			另一个用于从队列接收消息并通过串口打印。
* @Platform  		CSDN
* @author  			The_xzs
* @date    			2025.1.25
************************************/
// 定义一个消息结构体,用于存储消息ID和消息数据
struct AMessage
{char ucMessageID;        // 消息ID,使用1个字节的字符类型存储char ucData[20];         // 数据字段,使用20个字节的字符数组存储
};QueueHandle_t xQueue1, xQueue2; // 定义两个队列句柄变量// 发送任务函数
void vSenderTask(void *pvParameters)
{unsigned long ulValueToSend = 0; 				// 用于发送的无符号长整型变量struct AMessage *pxMessage;     				// 指向AMessage结构体的指针for( ;; ){ulValueToSend++; 							// 每次循环递增发送值pxMessage = (struct AMessage *) pvPortMalloc(sizeof(struct AMessage)); // 动态分配内存if(pxMessage != NULL) 								// 检查内存分配是否成功{pxMessage->ucMessageID = (char)ulValueToSend; 	// 设置消息IDsnprintf(pxMessage->ucData, 20, "Message %lu", ulValueToSend); // 格式化消息数据// 将无符号长整型值发送到队列xQueue1if( xQueueSend( xQueue1, &ulValueToSend, portMAX_DELAY ) != pdPASS ){// 如果发送失败,添加错误处理代码printf("xQueue1 failed to send\r\n");}// 将AMessage结构体的指针发送到队列xQueue2if( xQueueSend( xQueue2, &pxMessage, portMAX_DELAY ) != pdPASS ){printf("xQueue2 failed to send\r\n");}}vTaskDelay(1000 / portTICK_RATE_MS); // 延时1秒}
}// 接收任务函数
void vReceiverTask( void *pvParameters )
{unsigned long ulReceivedValue; // 用于接收的无符号长整型变量struct AMessage *pxReceivedMessage; // 指向AMessage结构体的指针for( ;; ) // 无限循环,任务持续运行{// 从队列xQueue1接收数据if( xQueueReceive( xQueue1, &ulReceivedValue, portMAX_DELAY ) == pdPASS ){// 如果接收成功,通过串口打印接收到的值printf("Received from xQueue1: %lu\r\n", ulReceivedValue);}// 从队列xQueue2接收数据if( xQueueReceive( xQueue2, &pxReceivedMessage, portMAX_DELAY ) == pdPASS ){// 如果接收成功,通过串口打印接收到的消息ID和数据printf("Received from xQueue2: ID = %d, Data = %s\r\n", pxReceivedMessage->ucMessageID, pxReceivedMessage->ucData);vPortFree(pxReceivedMessage); // 释放动态分配的内存}}
}/* 用于保存队列数据结构的静态变量。 */
static StaticQueue_t xStaticQueue;// 主函数
int main(void)
{Uart_Init(115200); DMA1_Init();      // 静态创建队列所需的存储空间static uint8_t ucQueueStorage1[10 * sizeof(unsigned long)];static uint8_t ucQueueStorage2[10 * sizeof(struct AMessage *)];/* 静态创建一个能够存储10个无符号长整型(unsigned long)的队列 */xQueue1 = xQueueCreateStatic(10, sizeof(unsigned long), ucQueueStorage1, &xStaticQueue);if( xQueue1 == NULL ) // 检查队列是否创建成功{/* 如果队列创建失败(内存分配失败),则不能使用这个队列 */while(1); // 无限循环,停止程序运行}/* 静态创建一个能够存储10个指向AMessage结构体的指针的队列这些结构体通过指针入队,因为它们相对较大 */xQueue2 = xQueueCreateStatic(10, sizeof(struct AMessage *), ucQueueStorage2, &xStaticQueue);if( xQueue2 == NULL ) // 检查队列是否创建成功{/* 如果队列创建失败(内存分配失败),则不能使用这个队列 */while(1); // 无限循环,停止程序运行}/* 创建发送和接收任务 */xTaskCreate(vSenderTask, "Sender", 64, NULL, 2, NULL); 		// 创建发送任务xTaskCreate(vReceiverTask, "Receiver", 64, NULL, 2, NULL); 	// 创建接收任务// 启动任务调度器,FreeRTOS开始执行任务的地方vTaskStartScheduler();  return 0;  // 理论上不会到达这里
}

效果:

五、队列创建时的常见问题:

(1)函数未定义:

  • 如果 configSUPPORT_STATIC_ALLOCATION设置为 1,则 RTOS 对象可以 通过应用程序编写者提供的 RAM 创建。
  • 如果 configSUPPORT_STATIC_ALLOCATION设置为 0,则 RTOS 对象 只能通过从 FreeRTOS 堆中分配的 RAM 创建。
  • 如果 configSUPPORT_STATIC_ALLOCATION未定义,则默认为 0。
  • 如果 configSUPPORT_STATIC_ALLOCATION设置为 1,则应用程序编写者还必须提供两个回调 函数:vApplicationGetIdleTaskMemory(),为 RTOS 空闲任务提供内存;(如果 configUSE_TIMERS设置为 1)vApplicationGetTimerTaskMemory(),为 RTOS 守护进程/定时器服务任务提供内存。

在task.h头文件可以看到该函数的声明:

 打开task.c文件打开找到该函数所在位置:

 向上滑动添加上该函数实现即可,如下:

(2)动态堆空间太大导致静态分配空间不足:

如下所示:

 原因:

在 FreeRTOS 中,configTOTAL_HEAP_SIZE 定义的堆空间是用于动态内存分配的区域。除了这个堆空间之外,系统中的其他内存空间可以用于静态内存分配。

默认定义的动态内存分配堆空间为17KB(c8t6):

只剩余3KB的内存空间用于静态内存分配导致空间不足。

解决办法减小,动态内存分配堆空间大小即可。

(3)队列创建后程序下载没反应:

静态创建队列中函数最后一个参数不能为NULL,且必须指向 StaticQueue_t 类型的变量因为该参数用于保存队列数据结构的静态变量。 

六、FreeRTOS教程示例代码下载:

FreeRTOS教程示例代码将会持续更新...

通过网盘分享的文件:FreeRTOS教程示例代码
链接: https://pan.baidu.com/s/1363h7hHmf8u2pjauwKyhtw?pwd=mi98 提取码: mi98

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/7615.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在data.table中处理缺失值

📊💻【R语言进阶】轻松搞定缺失值,让数据清洗更高效! 👋 大家好呀!今天我要和大家分享一个超实用的R语言技巧——如何在data.table中处理缺失值,并且提供了一个自定义函数calculate_missing_va…

基于OpenCV实现的答题卡自动判卷系统

一、图像预处理 🌄 二、查找答题卡轮廓 📏 三、透视变换 🔄 四、判卷与评分 🎯 五、主函数 六、完整代码+测试图像集 总结 🌟 在这篇博客中,我将分享如何使用Python结合OpenCV库开发一个答题卡自动判卷系统。这个系统能够自动从扫描的答题卡中提取信…

C++ list 容器用法

C list 容器用法 C 标准库提供了丰富的功能&#xff0c;其中 <list> 是一个非常重要的容器类&#xff0c;用于存储元素集合&#xff0c;支持双向迭代器。<list> 是 C 标准模板库&#xff08;STL&#xff09;中的一个序列容器&#xff0c;它允许在容器的任意位置快速…

docker部署jenkins

环境&#xff1a; centos7.9 jenkins/jenkins:lts-jdk11 1、拉去jenkins镜像&#xff0c;请指明版本号 [rootlocalhost ~]# docker pull jenkins/jenkins:lts 开始拉取 拉取完成 [rootlocalhost ~]# docker pull jenkins/jenkins:lts lts: Pulling from jenkins/jenkins 0a9…

沃尔玛 礼品卡绑定 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向分析 部分代码参考 func doPost…

【2024年华为OD机试】 (A卷,100分)- 整理扑克牌(JavaScriptJava PythonC/C++)

一、问题描述 题目描述 给定一组数字,表示扑克牌的牌面数字,忽略扑克牌的花色,请按如下规则对这一组扑克牌进行整理: 步骤1:分组形成组合牌 炸弹:当牌面数字相同张数大于等于4时。葫芦:3张相同牌面数字 + 2张相同牌面数字,且3张牌与2张牌不相同。三张:3张相同牌面数…

Arduino大师练成手册 --控制 OLED

要在 Arduino 上使用 U8glib 库控制带有 7 个引脚的 SPI OLED 显示屏&#xff0c;你可以按照以下步骤进行&#xff1a; 7pin OLED硬件连接 GND&#xff1a;连接到 Arduino 的 GND 引脚。 VCC&#xff1a;连接到 Arduino 的 5V 引脚。 D0&#xff08;或 SCK/CLK&#xff09;…

单片机基础模块学习——按键

一、按键原理图 当把跳线帽J5放在右侧&#xff0c;属于独立按键模式&#xff08;BTN模式&#xff09;&#xff0c;放在左侧为矩阵键盘模式&#xff08;KBD模式&#xff09; 整体结构是一端接地&#xff0c;一端接控制引脚 之前提到的都是使用了GPIO-准双向口的输出功能&#x…

AWScurl笔记

摘要 AWScurl是一款专为与AWS服务交互设计的命令行工具&#xff0c;它模拟了curl的功能并添加了AWS签名版本4的支持。这一特性使得用户能够安全有效地执行带有AWS签名的请求&#xff0c;极大地提升了与AWS服务交互时的安全性和有效性。 GitHub - okigan/awscurl: curl-like acc…

初识MySQL

文章目录 1.数据库2.查看数据库3.创建数据库4.字符集编码和排序规则6.修改数据库7.删除数据库 1.数据库 MySQL是一款使用率高且免费的数据库&#xff08;使用率仅仅低于Oracle&#xff09; 关系数据库和 NoSQL 数据库管理系统知识库(DB-Engines Ranking -) (此图数据于2025-1…

flutter_学习记录_00_环境搭建

1.参考文档 Mac端Flutter的环境配置看这一篇就够了 flutter的中文官方文档 2. 本人环境搭建的背景 本人的电脑的是Mac的&#xff0c;iOS开发&#xff0c;所以iOS开发环境本身是可用的&#xff1b;外加Mac电脑本身就会配置Java的环境。所以&#xff0c;后面剩下的就是&#x…

[b01lers2020]Life on Mars1

打开题目页面如下 看了旁边的链接&#xff0c;也没有什么注入点&#xff0c;是正常的科普 利用burp suite抓包&#xff0c;发现传参 访问一下 http://5edaec92-dd87-4fec-b0e3-501ff24d3650.node5.buuoj.cn:81/query?searchtharsis_rise 接下来进行sql注入 方法一&#xf…

【PyTorch】3.张量类型转换

个人主页&#xff1a;Icomi 在深度学习蓬勃发展的当下&#xff0c;PyTorch 是不可或缺的工具。它作为强大的深度学习框架&#xff0c;为构建和训练神经网络提供了高效且灵活的平台。神经网络作为人工智能的核心技术&#xff0c;能够处理复杂的数据模式。通过 PyTorch&#xff0…

机位:解锁摄影视角的多维度密码

目录 一、机位的构成要素 &#xff08;一&#xff09;高度维度 &#xff08;二&#xff09;角度维度 &#xff08;三&#xff09;距离维度 二、移动机位的魅力 &#xff08;一&#xff09;推镜头 &#xff08;二&#xff09;拉镜头 &#xff08;三&#xff09;摇镜头 …

【例51.3】 平移数据

题目描述 将a数组中第一个元素移到数组末尾,其余数据依次往前平移一个位置。 输入 第一行为数组a的元素个数&#xff1b; 第二行为n个小于1000的正整数。 输出 平移后的数组元素&#xff0c;每个数用一个空格隔开。 样例输入 复制 10 1 2 3 4 5 6 7 8 9 10 样例输出 复…

【Project】CupFox电影网站数据爬取分析与可视化

数据采集清洗与数据存储流程如下图所示。 数据分析与数据可视化流程设计如下 1.使用pymongo从数据库中查询所需的数据。对数据进行处理和分析&#xff0c;进行统计、分类、聚合等操作&#xff0c;提取关键指标和洞察。分析结果可以通过编写Python代码进一步优化、筛选和整理&a…

gradle创建springboot单项目和多模块项目

文章目录 gradle创建springboot项目gradle多模块项目创建 gradle创建springboot项目 适用IDEA很简单&#xff0c;如下图 gradle多模块项目创建 首选创建父项目&#xff0c;然后删除无用内容至下图 选择父项目目录&#xff0c;右键选择模块&#xff0c;创建子项目&#xff08…

数据库的JOIN连接查询算法

文章目录 3.2 Join 算法优化3.1.2 Nested Loop Join&#xff08;NLJ&#xff09;3.1.3 Block Nested Loop Join&#xff08;BNLJ&#xff09;3.1.4 Index Nested Loop Join&#xff08;INLJ&#xff09;3.1.5 Sort Merge Join&#xff08;SMJ&#xff09;3.1.6 Hash Join 3.2 J…

Golang Gin系列-8:单元测试与调试技术

在本章中&#xff0c;我们将探讨如何为Gin应用程序编写单元测试&#xff0c;使用有效的调试技术&#xff0c;以及优化性能。这包括设置测试环境、为处理程序和中间件编写测试、使用日志记录、使用调试工具以及分析应用程序以提高性能。 为Gin应用程序编写单元测试 设置测试环境…

二叉树的最大深度(C语言详解版)

一、摘要 嗨喽呀大家&#xff0c;leetcode每日一题又和大家见面啦&#xff0c;今天要讲的是104.二叉树的最大深度&#xff0c;思路互相学习&#xff0c;有什么不足的地方欢迎指正&#xff01;好啦让我们开始吧&#xff01;&#xff01;&#xff01; 二、题目简介 给定一个二…