初始化,构造和转换struct到byte数组会导致错位

时间:2015-06-13 14:54:11

标签: c arrays pointers struct memcpy

我正在尝试设计一个数据结构(我已经把它缩短了很多,以节省空间,但我认为你得到了这个想法)用于字节级通信:

/* PACKET.H */    
#define CM_HEADER_SIZE          3
#define CM_DATA_SIZE            16
#define CM_FOOTER_SIZE          3
#define CM_PACKET_SIZE       (CM_HEADER_SIZE + CM_DATA_SIZE + CM_FOOTER_SIZE)
// + some other definitions

typedef struct cm_header{  
  uint8_t       PacketStart;   //Start Indicator 0x5B [
  uint8_t       DeviceId;       //ID Of the device which is sending
  uint8_t       PacketType;
} CM_Header;

typedef struct cm_footer {
  uint16_t      DataCrc;        //CRC of the 'Data' part of CM_Packet
  uint8_t       PacketEnd;      //should be 0X5D or ]
} CM_Footer;

//Here I am trying to conver a few u8[4] tp u32 (4*u32 = 16 byte, hence data size)
typedef struct cm_data {
  union {
      struct{
        uint8_t           Value_0_0:2;
        uint8_t           Value_0_1:2;
        uint8_t           Value_0_2:2;
        uint8_t           Value_0_3:2;          
      };
    uint32_t              Value_0;
  };
  //same thing for Value_1, 2 and 3
} CM_Data;

typedef struct cm_packet {
  CM_Header Header;
  CM_Data Data;
  CM_Footer Footer;
} CM_Packet;

typedef struct cm_inittypedef{
  uint8_t               DeviceId;
  CM_Packet             Packet;        
} CM_InitTypeDef;

typedef struct cm_appendresult{
  uint8_t Result;
  uint8_t Reason;
} CM_AppendResult;

extern CM_InitTypeDef cmHandler;

这里的目标是建立可靠的结构,通过USB接口传输数据。最后,CM_Packet应转换为uint8_t数组,并提供给mcu(stm32)的数据发送寄存器。

main.c文件中,我尝试初始化结构以及与此数据包相关的其他一些内容:

/* MAIN.C */
uint8_t packet[CM_PACKET_SIZE];
int main(void) {
    //use the extern defined in packet.h to init the struct
    cmHandler.DeviceId = 0x01; //assign device id
    CM_Init(&cmHandler); //construct the handler
    //rest of stuff   

    while(1) {
        CM_GetPacket(&cmHandler, (uint8_t*)packet);
        CDC_Transmit_FS(&packet, CM_PACKET_SIZE);
    }
}

这里是packet.h的实现,它搞砸了一切都很糟糕。我添加了packet[CM_PACKET_SIZE]来观看,但它就像是随机生成的。有时通过纯粹的运气我可以在这个数组中看到一些我感兴趣的值!但它有点像1%的时间!

/* PACKET.C */
CM_InitTypeDef cmHandler;

void CM_Init(CM_InitTypeDef *cm_initer) {
  cmHandler.DeviceId = cm_initer->DeviceId;

  static CM_Packet cmPacket;
  cmPacket.Header.DeviceId = cm_initer->DeviceId;
  cmPacket.Header.PacketStart = CM_START;
  cmPacket.Footer.PacketEnd = CM_END;
  cm_initer->Packet = cmPacket;
}

CM_AppendResult CM_AppendData(CM_InitTypeDef *handler, uint8_t identifier, 
                              uint8_t *data){    

    CM_AppendResult result;

    switch(identifier){
      case CM_VALUE_0:
        handler->Packet.Data.Value_0_0 = data[0];
        handler->Packet.Data.Value_0_1 = data[1];
        handler->Packet.Data.Value_0_2 = data[2];
        handler->Packet.Data.Value_0_3 = data[3];
        break;

       //Also cases for CM_VALUE_0, 1 , 2
       //to build up the CM_Data sturct of CM_Packet

    default:
      result.Result = CM_APPEND_FAILURE;
      result.Reason = CM_APPEND_CASE_ERROR;
      return result;
      break;

    }    

    result.Result = CM_APPEND_SUCCESS;
    result.Reason = 0x00;
    return result;
}

void CM_GetPacket(CM_InitTypeDef *handler, uint8_t *packet){
    //copy the whole struct in the given buffer and later send it to USB host
    memcpy(packet, &handler->Packet, sizeof(CM_PACKET_SIZE));
}

所以,问题是这段代码给了我99%的随机时间。它永远不会有CM_START这是我想要的值的数据包的开始指示符。但大多数时候它正确地有CM_END个字节!我真的很困惑,无法找出原因。正在开发一个难以调试的嵌入式平台,我在这里迷失了......

1 个答案:

答案 0 :(得分:1)

如果您将数据传输到另一个(不同的)体系结构,请不要只将结构作为blob传递。这是地狱的方式:endianess,alignment,padding bytes等等都可能(并且可能会)造成麻烦。

更好地以一致的方式序列化结构,可能使用一些解释的控制流,因此您不必手动编写每个字段。 (但仍然使用标准函数来生成该流)。

某些潜在或可能出现问题的区域:

  • CM_Footer:第二个字段可能很好地从32或64位边界开始,因此前面的字段将跟随填充。此外,该结构的结尾很可能在32位体系结构上至少填充1个字节,以便在数组中使用时允许正确对齐(如果您确实需要,编译器不关心您)。甚至可能是8字节对齐。
  • CM_Header:在这里你可能(不保证)得到一个带有4 * 2位的uint8_t,其顺序未标准化。字段后面跟着3个未使用的字节,这是uint32_t对union的解释所必需的。
  • 如何为主机和目标保证相同的字节顺序(对于> uint8_t:高字节优先或低字节优先?)?
  • 通常,结构/联合不需要具有相同的主机和目标布局。即使使用相同的编译器,它们的ABI也可能不同,等等。即使它是相同的CPU,也可能存在其他系统限制。此外,对于某些CPU,存在不同的ABI(应用程序二进制接口)。