FreeRTOS文档与QueueSets一起讨论了该问题。 (看到 https://www.freertos.org/Pend-on-multiple-rtos-objects.html 在“使用队列集的替代方法”一节中。 基本上,我们的想法是使用枚举来识别缓冲区中不同类型的消息。
对于缓冲区类型,您可以使用枚举和结构联合的组合。
struct A {uint32_t foo;}; struct B {uint8_t bar;}; enum T {AType, BType}; struct GenericMessage{ T type; union{ struct A a; struct B b; }; };
然后您可以区分消息如下:
void handleGenericMessage(struct GenericMessage* msg){ switch(msg->type){ case AType: handleA(msg->a); break; case BType: handleB(msg->b); break; }
您可以考虑使用指向消息的指针队列,而不是消息队列本身。这样,队列中的每个项目都是固定大小(指针的大小),而不管消息如何。
该技术确实需要仔细的资源管理,以确保在接收器完成之前不会修改,删除或重用消息。您需要一个allocate-enqueue-dequeue-deallocate机制。
例如,给定:
enum eMessageType { AType, BType }; struct GenericMessage { eMessageType type; char payload[0] ; // Note GCC zero-length array extension }; struct A { struct GenericMessage, uint32_t foo; }; struct B { struct GenericMessage, uint8_t bar; };
然后发件人可能会有(伪代码):
struct A* messageA = allocateMessageA( AType, 0x12345678 ) ; struct B* messageB = allocateMessageB{ BType, 0x12 } ; sendGeneric( genericQ, messageA ) ; sendGeneric( genericQ, messageB ) ;
和接收者:
struct GenericMessage* message_ptr = receiveGeneric( genericQ ) ; switch( message_ptr->type ) { case AType: { struct A* = (struct A*)message_ptr ; uint32_t payload = message_ptr->payload ; ... deallocateMessageA( message_ptr ) ; } break; case BType: { struct B* = (struct B*)message_ptr ; uint8_t payload = message_ptr->payload ; ... deallocateMessageB( message_ptr ) ; } }
我没有定义的分配/释放功能的细节。一种简单的方法是使用固定块内存池。如果您的RTOS不提供这些,则一个简单的实现是在池中具有指向消息的队列,每个消息类型一个队列/池。要分配,只需从队列中取消一条消息,从相关消息池中获取指针,然后通过返回指向队列的指针来解除分配。
请注意,某些RTOS允许直接发送可变长度消息 - 例如,您可以为队列分配内存池,并在发送时指定长度,而不是在创建队列时指定长度。然而,接收器必须能够接收尽可能大的消息。