cembeddedcan-busiartype-punning

Linking two message structures together


I have two slightly different structures which i would like to somehow link for fastest results.

These are the two structues:

/** 
  * @brief  CAN Tx message structure definition  
  */
typedef struct
{
  uint32_t StdId;    /*!< Specifies the standard identifier.
                          This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF */

  uint32_t ExtId;    /*!< Specifies the extended identifier.
                          This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF */

  uint32_t IDE;      /*!< Specifies the type of identifier for the message that will be transmitted.
                          This parameter can be a value of @ref CAN_Identifier_Type */

  uint32_t RTR;      /*!< Specifies the type of frame for the message that will be transmitted.
                          This parameter can be a value of @ref CAN_remote_transmission_request */

  uint32_t DLC;      /*!< Specifies the length of the frame that will be transmitted.
                          This parameter must be a number between Min_Data = 0 and Max_Data = 8 */

  uint8_t Data[8];   /*!< Contains the data to be transmitted.
                          This parameter must be a number between Min_Data = 0 and Max_Data = 0xFF */

}CanTxMsgTypeDef;

/**
  * @brief  CAN Rx message structure definition
  */
typedef struct
{
  uint32_t StdId;       /*!< Specifies the standard identifier.
                             This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF */

  uint32_t ExtId;       /*!< Specifies the extended identifier.
                             This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF */

  uint32_t IDE;         /*!< Specifies the type of identifier for the message that will be received.
                             This parameter can be a value of @ref CAN_Identifier_Type */

  uint32_t RTR;         /*!< Specifies the type of frame for the received message.
                             This parameter can be a value of @ref CAN_remote_transmission_request */

  uint32_t DLC;         /*!< Specifies the length of the frame that will be received.
                             This parameter must be a number between Min_Data = 0 and Max_Data = 8 */

  uint8_t Data[8];      /*!< Contains the data to be received.
                             This parameter must be a number between Min_Data = 0 and Max_Data = 0xFF */

  uint32_t FMI;         /*!< Specifies the index of the filter the message stored in the mailbox passes through.
                             This parameter must be a number between Min_Data = 0 and Max_Data = 0xFF */

  uint32_t FIFONumber;  /*!< Specifies the receive FIFO number.
                             This parameter can be CAN_FIFO0 or CAN_FIFO1 */

}CanRxMsgTypeDef;

They are a part of ST drivers used at IAR embedded workbench for ARM, so I can't change them.

My function is mostly doing filtering which means that whatever I receive, I need to transmit almost immediately.

Driver functions (which I also can't change) only allow transmitting CanTxMsgTypeDef types. So each time I need to copy CanRxMsgTypeDef variable to CanTxMsgTypeDef variable which is very inefficient.

I am looking for best practice for fastest results i.e. transmitting CanRxMsgTypeDef data with minimum copy-paste.

Please note how CanRxMsgTypeDef has only extra data i.e. all the information to transmit already at the received CanRxMsgTypeDef variable.


Solution

  • Use union of the two structs and you can access any member from the common initial part as per 6.5.2.3p6:

    One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the completed type of the union is visible. Two structures share a common initial sequence if corresponding members have compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.

    Sample code:

    union CanMsgTypeDef
    {
        CanRxMsgTypeDef rx;
        CanTxMsgTypeDef tx;
    };
    
    void someFunc(CanTxMsgTypeDef arg)
    {
        printf("inside func tx.StdId=%d\n",arg.StdId);
        printf("inside func tx.DLC=%d\n",arg.DLC);
    }
    
    int main()
    {
        /* Assign RX type to the union */
        union CanMsgTypeDef test = {1,2,3,4,5,{0,0,0,0,0,0,0,0},7,8};
    
        printf("test.tx.StdId=%d\n",test.tx.StdId);
        printf("test.rx.StdId=%d\n",test.rx.StdId);
        printf("test.tx.DLC=%d\n",test.tx.DLC);
        printf("test.rx.DLC=%d\n",test.rx.DLC);
        printf("test.rx.FMI=%d\n",test.rx.FMI);
    
        /* Use the union as tx type */
        someFunc(test.tx);
        return 0;
    }