代码之家  ›  专栏  ›  技术社区  ›  chris12892

嵌入式RTOS生产者和消费者,具有多种类型的消息[关闭]

  •  0
  • chris12892  · 技术社区  · 7 年前

    许多RTOS消息传递示例显示,生产者生成了简单的int32,消费者读取了它。这很简单,因为您总是知道队列(或其他消息传递缓冲区)的类型和大小。

    考虑RTOS线程从GPS读取数据的情况。GPS可能有多种类型的信息。您不能保证消息何时到达、消息的顺序或消息的大小。每个消息将有许多字段,并且每个消息可以表示为一个结构。

    这个GPS处理生产者RTOS线程将理想地解析进入的数据流,然后将数据传递出去,这样消费者线程就可以轻松地读取GPS数据。

    假设freertos或cmsis rtos。如何才能最好地建立这种安排?将使用哪些原语?从根本上讲,我要寻找的是一种从生产者线程中传递几种不同类型的不同大小的结构并在消费者中读取它们的干净方法。

    2 回复  |  直到 7 年前
        1
  •  3
  •   Clifford    7 年前

    您可以考虑使用指向消息的指针队列,而不是消息本身的队列。这样,不管消息如何,队列中的每个项目都是固定大小(指针大小)。

    这项技术需要仔细的资源管理,以确保在接收器完成消息处理之前,消息不会被修改、删除或重用。您需要一个分配队列出列释放机制。

    例如,给定:

    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 ) ;
        }
    }  
    

    我尚未定义的分配/解除分配函数的详细信息。一个简单的方法是使用固定块内存池。如果您的RTO不提供这些,一个简单的实现是在池中有指向消息的指针队列,每个消息类型有一个队列/池。要分配,只需从相关消息池中获取一个指针,将消息从队列中取出,然后通过返回指向队列的指针来取消分配。

    注意,有些RTO允许直接发送可变长度的消息——例如,在EMBOS中,您可以为队列分配内存池,并在发送时指定长度,而不是在创建队列时指定长度。但是,接收器必须能够接收最大可能的消息。

        2
  •  4
  •   Alexander    7 年前

    Freertos文档与队列集一起讨论这个问题。(见 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;
    }