队列集合

freeRTOS通过队列集合(Queue Sets)允许任务同时阻塞在多个队列或者信号量上。队列和信号量以集合的形式组织。

注意:尽管在集成第三方的服务时,有时阻塞在多个队列上是必要的,但是还有很多其他的设计模式可以高效完成相同的功能,详细见文章底部部分内容。

使用队列集合

队列集合在使用方法上和select()这类API函数很像,它们都是标准的Berkeley sockets networking API的一部分。

队列集合可以包含队列和信号量,它们都是队列集合的成员。API函数的参数及返回值可以是队列或者信号量句柄之一,使用QueueSetMemberHandle_t自定义类型。对于QueueHandle_tSemaphoreHandle_t类型的变量可以隐式的转换为QueueSetMemberHandle_t而不会导致任何编译器警告,因此这种数据类型的转换可以不用显示的强制转换,我的想法:凡是涉及到数据类型的转换,最好显示转换,一方面不会出错,另一方面也可以让阅读你的代码的人知道这是你故意为之,而不会包含其他歧义。
















创建队列集合


在使用队列集合之前必须使用
xQueueCreateSet() API函数创建集合队列.一旦集合队列创建完成,以后可以通过数据类型为QueueSetHandle_t的句柄访问。




向队列集合添加成员


使用 xQueueAddToSet() API 添加成员到队列集合中




在集合队列上挂起


xQueueSelectFromSet() API 函数 用来测试集合中的成员是否可以”读取”—“读取”对于队列来说就是可以接收,对于信号量来说就是可以获取。.


就像使用 xQueueReceive()
xSemaphoreTake() API函数一样, xQueueSelectFromSet()
允许调用的任务选择性的挂起直到队列成员可读。


调用xQueueSelectFromSet()等待读取超时时返回NULL. 否则返回相应的可读取的集合成员句柄,此时调用xQueueReceive()或者xSemaphoreTake()一定可以获取到相关的队列或信号量


示例代码

所有的API,官方都提供了一个详细的说明,因为数量众多,这部分内容不打算翻译,有需要的可以访问官网获取这部分内容

xQueueCreateSet()API文档中包含一个示例的源代码,同时在下载的源代码文件的路径FreeRTOS/Demo/Common/Minimal/下提供一个命名为QueueSet.c的demo源代码。

使用集合队列的替代项

除非确实有特定的集成问题而必须阻塞在多个队列上,使用单一队列实现可以节省更过的ROM、RAM和运行时间。freeRTOS+UDP的实现方式提供了这样的一个替代队列集合的例子:

UDP/IP Stack: 问题定义

管理freeRTOS+UDP堆栈的任务是事件驱动的,而事件源有多个,有些有相应的数据源绑定,而有些则没有。事件包括:

  • 网卡收到数据帧,数据帧中包含大量的数据及变量。
  • 网卡完成数据的传输,释放网络和DMA缓冲区。
  • 应用发送一个包含很多数据的数据包。
  • 软件定时器的定时事件,此事件没有相联系的数据。

UDP/IP Stack: 解决方法

UDP/IP 栈使用不同的队列来存储事件源,然后使用同一个队列用以设置阻塞:

  • 定义一个结构体用来包含事件类型和与此事件相关的数据指针。
  • 使用同一个队列来保存上面定义的结构体,每个事件都会被发送到同一个队列。

结构体定义如下:

  1. typedef struct IP_TASK_COMMANDS
  2. {
  3. eIPEvent_t eEventType; /* 事件类型. */
  4. void *pvData; /* 事件关联数据指针 */
  5. } xIPStackEvent_t;

这样就很方便实现一个事件驱动的程序结构,根据事件类型选择处理方式和相关数据:

  1. /* The variable used to receive from the queue. */
  2. xIPStackEvent_t xReceivedEvent;
  3. for( ;; )
  4. {
  5. /* 等待队列中数据有效,否则一直挂起 */
  6. xQueueReceive( xNetworkEventQueue, &xReceivedEvent, portMAX_DELAY );
  7. /* 根据事件类型不同,选择相应处理方式. */
  8. switch( xReceivedEvent.eEventType )
  9. {
  10. case eNetworkDownEvent :
  11. prvProcessNetworkDownEvent();
  12. break;
  13. case eEthernetRxEvent :
  14. prvProcessEthernetFrame( xReceivedEvent.pvData );
  15. break;
  16. case eARPTimerEvent :
  17. prvAgeARPCache();
  18. break;
  19. case eStackTxEvent :
  20. prvProcessGeneratedPacket( xReceivedEvent.pvData );
  21. break;
  22. case eDHCPEvent:
  23. vDHCPProcess();
  24. break;
  25. default :
  26. /* 这里应该不会被执行到,否则对事件类型的分类不完整 */
  27. break;
  28. }
  29. }