【问题标题】:How to know the type of the data being sent from task B to task A?如何知道从任务 B 发送到任务 A 的数据类型?
【发布时间】:2020-12-12 15:55:43
【问题描述】:

假设我有一个任务 (taskA) 接受来自另一个任务 (taskB) 的数据。如何使 taskA 可以接受泛型类型? void 数组是不允许的,所以这是不可能的。

void buff[100];  // not allowed !!

void taskB(void *params)
{
  while(1) {
    xQueueReceive(queueA, buff, portMAX_DELAY);
  }

}

void taskA(void *params) 
{
   char data[] = "something";
   while(1) {
     xQueueSend(queueA, data, portMAX_DELAY);
   }
}

【问题讨论】:

  • 这只是一个正常的数据序列化问题。实现它的一般方法是通过普通缓冲区(通常为unsigned char)流式传输数据,并在通信协议中保留一些位,以指示正在传输的实际数据类型。然后,您可以使用适合发送方编码方式的任何解码方法对比特流进行解码以提取这些值。在整数数据的情况下,您可能只是将它们作为原始字节传输。
  • 不确定为什么需要使用unsigned char 来发送缓冲区。如果要发送double 的数据类型怎么办?并且通过保留在协议中传输的数据,您指的是存储在我认为的结构中?
  • 不,我不是指使用结构,而且您似乎不了解这里的基本原理。你只需要交付字节。如何选择在这些字节中表示数据取决于您。您可以将double 值放入字节流中,可以作为原始字节或以某种方式编码,也可以作为文本或任何您想要的。关键是接收者需要知道接下来的 X 字节将包含一个 double 值,然后从它接收到的数据中正确解码它,因此为什么要将该信息添加到字节流中。
  • 所以你说这个想法是发送数据字节,而接收者应该知道正在发送什么数据类型?但是如果发送者任务没有传递具有数据类型的结构,接收者如何知道正在发送的数据呢?
  • 您发送的某些字节提供了有关您发送的数据类型的信息。因此,当接收者获得该信息时,它就知道该做什么了。我反复告诉你这一点,我不知道为什么它没有深入人心。

标签: c embedded freertos


【解决方案1】:

一个选项是使用“字节数组”(例如unsigned char 数组)并从中复制您需要的任何数据(注意不要引入未定义的行为)。您可以使用第一个字节来知道您发送了什么样的数据。

另一个是在struct 中使用union 和一个附加变量(在struct 中),该变量包含一个“标签”,告诉您union 中有哪些类型的数据。

两者本质上是一样的。

【讨论】:

  • unsigned char in taskB?
  • 在您用来接收/发送数据的任何缓冲区中。
  • 关于你的第二个选项,你的意思是taskA 发送一个结构而不是一个数组,其中的数据类型将在taskB 中用于解码的结构成员中?
  • 是的,无论如何,你必须发送一些东西,让你在接收者中知道你发送了什么类型。
  • 为什么特别是unsigned char
【解决方案2】:

如果类型容易频繁变化(b/w不同的调用),一个简单的方法是,将类型数据添加到函数装饰中,例如;

typedef enum {enumInt, enumDbl, enumChr, ... <add other types>} TypeInfo;

void taskB(void *ptr, TypeInfo t) {
    switch (t) {
        case enumInt:
            int *p = (int *)ptr;
            // do stuff as if data is integer
            break;
        case enumDbl:
            double *p = (double *)ptr;
            // do stuff as if data is double
            break;
        case enumChr:
            char *p = (char *)ptr;
            // do stuff as if data is char
            break;
    }
}

void taskA(void *ptr, TypeInfo t) {
    ...
    taskB(ptr, t);
    ...
}

【讨论】:

  • 对枚举中的所有类型进行硬编码是否方便?如果你错过了怎么办?
  • @xyf : 是的,如果类型太多,尤其是像structs 等非标准类型,那么你是对的...... switchif 块将是一团糟。
  • 不确定这是否适用于我的情况,尽管taskA 会写入taskB 会读取的队列,这不像调用函数。任务是指一个线程
  • @xyf 如果您在嵌入式 RTOS 系统中有这么多类型和抽象级别,我会说您的整体程序设计有些问题。这些系统应该是确定性的,而不是随机通用的。
  • @xyf 然后使用原始uint8_t 字节流。你只需要一个枚举、一个数据指针和一个可选的大小变量。
【解决方案3】:

请参阅以下注释良好的示例。

您可以编辑xMessage 结构来存储您想要发送/接收的任何数据。

这个例子也以两种方式发送消息:发送分配缓冲区的地址和发送缓冲区的值。您必须考虑使用您的程序以哪种方式子表。

请注意,该项目是通过副本接收的,因此必须提供足够大小的缓冲区。复制到缓冲区的字节数是在创建队列时定义的。

参考:https://www.freertos.org/a00118.html

/* Define a variable of type struct AMMessage.  The examples below demonstrate
how to pass the whole variable through the queue, and as the structure is
moderately large, also how to pass a reference to the variable through a queue. */
struct AMessage
{
   char ucMessageID;
   char ucData[ 20 ];
} xMessage;

/* Queue used to send and receive complete struct AMessage structures. */
QueueHandle_t xStructQueue = NULL;

/* Queue used to send and receive pointers to struct AMessage structures. */
QueueHandle_t xPointerQueue = NULL;


void vCreateQueues( void )
{
   xMessage.ucMessageID = 0xab;
   memset( &( xMessage.ucData ), 0x12, 20 );

   /* Create the queue used to send complete struct AMessage structures.  This can
   also be created after the schedule starts, but care must be task to ensure
   nothing uses the queue until after it has been created. */
   xStructQueue = xQueueCreate(
                         /* The number of items the queue can hold. */
                         10,
                         /* Size of each item is big enough to hold the
                         whole structure. */
                         sizeof( xMessage ) );

   /* Create the queue used to send pointers to struct AMessage structures. */
   xPointerQueue = xQueueCreate(
                         /* The number of items the queue can hold. */
                         10,
                         /* Size of each item is big enough to hold only a
                         pointer. */
                         sizeof( &xMessage ) );

   if( ( xStructQueue == NULL ) || ( xPointerQueue == NULL ) )
   {
      /* One or more queues were not created successfully as there was not enough
      heap memory available.  Handle the error here.  Queues can also be created
      statically. */
   }
}

/* Task that writes to the queues. */
void vATask( void *pvParameters )
{
struct AMessage *pxPointerToxMessage;

   /* Send the entire structure to the queue created to hold 10 structures. */
   xQueueSend( /* The handle of the queue. */
               xStructQueue,
               /* The address of the xMessage variable.  sizeof( struct AMessage )
               bytes are copied from here into the queue. */
               ( void * ) &xMessage,
               /* Block time of 0 says don't block if the queue is already full.
               Check the value returned by xQueueSend() to know if the message
               was sent to the queue successfully. */
               ( TickType_t ) 0 );

   /* Store the address of the xMessage variable in a pointer variable. */
   pxPointerToxMessage = &xMessage;

   /* Send the address of xMessage to the queue created to hold 10    pointers. */
   xQueueSend( /* The handle of the queue. */
               xPointerQueue,
               /* The address of the variable that holds the address of xMessage.
               sizeof( &xMessage ) bytes are copied from here into the queue. As the
               variable holds the address of xMessage it is the address of xMessage
               that is copied into the queue. */
               ( void * ) &pxPointerToxMessage,
               ( TickType_t ) 0 );

   /* ... Rest of task code goes here. */
}

/* Task that reads from the queues. */
void vADifferentTask( void *pvParameters )
{
struct AMessage xRxedStructure, *pxRxedPointer;

   if( xStructQueue != NULL )
   {
      /* Receive a message from the created queue to hold complex struct AMessage
      structure.  Block for 10 ticks if a message is not immediately available.
      The value is read into a struct AMessage variable, so after calling
      xQueueReceive() xRxedStructure will hold a copy of xMessage. */
      if( xQueueReceive( xStructQueue,
                         &( xRxedStructure ),
                         ( TickType_t ) 10 ) == pdPASS )
      {
         /* xRxedStructure now contains a copy of xMessage. */
      }
   }

   if( xPointerQueue != NULL )
   {
      /* Receive a message from the created queue to hold pointers.  Block for 10
      ticks if a message is not immediately available.  The value is read into a
      pointer variable, and as the value received is the address of the xMessage
      variable, after this call pxRxedPointer will point to xMessage. */
      if( xQueueReceive( xPointerQueue,
                         &( pxRxedPointer ),
                         ( TickType_t ) 10 ) == pdPASS )
      {
         /* *pxRxedPointer now points to xMessage. */
      }
   }

   /* ... Rest of task code goes here. */
}

【讨论】:

  • 对,所以你在两端使用相同的结构类型,这很好,但为什么要使用char 来特别发送数据(ucData)?如果要发送double 类型的数据怎么办?
  • 一切都可以被视为字节数组,所以你可以将ucData定义为unsigned char*,如果你想发送一个double数组你可以使用ucData = unsigned char*) double_array;,但是在在这种情况下,您必须动态分配ucData 指向的数组并通过队列按地址发送。接收队列也负责释放ucData
  • 为什么要动态分配ucData?从doubleunsigned char 的转换是否有效?
  • 如果你的数据没有提前确定,就得动态分配缓冲区。 [xyf] 并且从 double 转换为 unsigned char 是否有效?是的,如果没有,编译器会显示警告
  • 我不确定我是否明白你的意思:onlinegdb.com/r1ef5YBZQP
猜你喜欢
  • 1970-01-01
  • 2022-12-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多