队列集合
freeRTOS通过队列集合(Queue Sets)允许任务同时阻塞在多个队列或者信号量上。队列和信号量以集合的形式组织。
注意:尽管在集成第三方的服务时,有时阻塞在多个队列上是必要的,但是还有很多其他的设计模式可以高效完成相同的功能,详细见文章底部部分内容。
使用队列集合
队列集合在使用方法上和select()
这类API函数很像,它们都是标准的Berkeley sockets networking API的一部分。
队列集合可以包含队列和信号量,它们都是队列集合的成员。API函数的参数及返回值可以是队列或者信号量句柄之一,使用QueueSetMemberHandle_t
自定义类型。对于QueueHandle_t
和SemaphoreHandle_t
类型的变量可以隐式的转换为QueueSetMemberHandle_t
而不会导致任何编译器警告,因此这种数据类型的转换可以不用显示的强制转换,我的想法:凡是涉及到数据类型的转换,最好显示转换,一方面不会出错,另一方面也可以让阅读你的代码的人知道这是你故意为之,而不会包含其他歧义。。
创建队列集合 | 在使用队列集合之前必须使用 xQueueCreateSet() API函数创建集合队列.一旦集合队列创建完成,以后可以通过数据类型为QueueSetHandle_t的句柄访问。
|
向队列集合添加成员 | 使用 xQueueAddToSet() API 添加成员到队列集合中
|
在集合队列上挂起 | xQueueSelectFromSet() API 函数 用来测试集合中的成员是否可以”读取”—“读取”对于队列来说就是可以接收,对于信号量来说就是可以获取。.
|
示例代码
所有的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 栈使用不同的队列来存储事件源,然后使用同一个队列用以设置阻塞:
- 定义一个结构体用来包含事件类型和与此事件相关的数据指针。
- 使用同一个队列来保存上面定义的结构体,每个事件都会被发送到同一个队列。
结构体定义如下:
typedef struct IP_TASK_COMMANDS
{
eIPEvent_t eEventType; /* 事件类型. */
void *pvData; /* 事件关联数据指针 */
} xIPStackEvent_t;
这样就很方便实现一个事件驱动的程序结构,根据事件类型选择处理方式和相关数据:
/* The variable used to receive from the queue. */
xIPStackEvent_t xReceivedEvent;
for( ;; )
{
/* 等待队列中数据有效,否则一直挂起 */
xQueueReceive( xNetworkEventQueue, &xReceivedEvent, portMAX_DELAY );
/* 根据事件类型不同,选择相应处理方式. */
switch( xReceivedEvent.eEventType )
{
case eNetworkDownEvent :
prvProcessNetworkDownEvent();
break;
case eEthernetRxEvent :
prvProcessEthernetFrame( xReceivedEvent.pvData );
break;
case eARPTimerEvent :
prvAgeARPCache();
break;
case eStackTxEvent :
prvProcessGeneratedPacket( xReceivedEvent.pvData );
break;
case eDHCPEvent:
vDHCPProcess();
break;
default :
/* 这里应该不会被执行到,否则对事件类型的分类不完整 */
break;
}
}