0

I'm trying to encode a relatively complex message into a union structure so that I can generate an array of uint8_t that I can feed into a serial communications protocol.

However, when looking at the array generated when my union is filled with data, an extra uint8_t element appears after my command type element. Please see the Union structure below:

union myint16_t{
        uint16_t ui16;
        int16_t i16;
        uint8_t data[2];
    };

union {
    struct {
        uint8_t commandtype;
        myint16_t UpperLimits[4];
        myint16_t LowerLimits[4];
        myint16_t JointZeros[4];
        int8_t JointPolarity[4];
        myint16_t P[4];
        myint16_t I[4];
        myint16_t D[4];

    };
    uint8_t data[53];
};

as you can see there are the second anonymous union references the union myint16_t. So if I fill all the values of the anonymous union and then print out the underlying data[53] array, my second value (data[1]) is 0 and not part of the first element of UpperLimits[4]. Please see the code where I fill the union and then print out the elements.

char q = 'c';



    hmmv4_configmsg msg;

msg.commandtype =(uint8_t) q;
msg.UpperLimits[0].ui16 = 784;
msg.UpperLimits[1].ui16  = 784;
msg.UpperLimits[2].ui16  = 784;
msg.UpperLimits[3].ui16  = 784;
msg.LowerLimits[0].ui16  = 223;
msg.LowerLimits[1].ui16  = 223;
msg.LowerLimits[2].ui16  = 223;
msg.LowerLimits[3].ui16  = 223;
msg.JointZeros[0].ui16  = 512;
msg.JointZeros[1].ui16  = 512;
msg.JointZeros[2].ui16  = 512;
msg.JointZeros[3].ui16  = 512;
msg.JointPolarity[0] = -1;
msg.JointPolarity[1] =-1;
msg.JointPolarity[2] =-1;
msg.JointPolarity[3] =-1;
msg.P[0].i16=4000;
msg.P[1].i16=4000;
msg.P[2].i16=4000;
msg.P[3].i16=4000;
msg.I[0].i16=1;
msg.I[1].i16=1;
msg.I[2].i16=1;
msg.I[3].i16=1;
msg.D[0].i16=24;
msg.D[1].i16=24;
msg.D[2].i16=24;
msg.D[3].i16=24;

//msg.change_endian();

while(1)
{
        for(int i =0; i<54; i++)
        {
            writebuf[i]=msg.data[i];

            printf("D: %d,  %d \n", i, msg.data[i]);
        }

        printf("L0: %d, %d, %d", msg.P[0].i16, msg.P[0].data[0], msg.P[0].data[1]);

        int r =jointencoder.xfer1(writebuf, readbuf, 54);
}

the output of the printf is (the offending element is D:1 -- it shouldn't be there):

D: 0,  99 
D: 1,  0 
D: 2,  16 
D: 3,  3 
D: 4,  16 
D: 5,  3 
D: 6,  16 
D: 7,  3 
D: 8,  16 
D: 9,  3 
D: 10,  223 
D: 11,  0 
D: 12,  223 
D: 13,  0 
D: 14,  223 
D: 15,  0 
D: 16,  223 
D: 17,  0 
D: 18,  0 
D: 19,  2 
D: 20,  0 
D: 21,  2 
D: 22,  0 
D: 23,  2 
D: 24,  0 
D: 25,  2 
D: 26,  255 
D: 27,  255 
D: 28,  255 
D: 29,  255 
D: 30,  160 
D: 31,  15 
D: 32,  160 
D: 33,  15 
D: 34,  160 
D: 35,  15 
D: 36,  160 
D: 37,  15 
D: 38,  1 
D: 39,  0 
D: 40,  1 
D: 41,  0 
D: 42,  1 
D: 43,  0 
D: 44,  1 
D: 45,  0 
D: 46,  24 
D: 47,  0 
D: 48,  24 
D: 49,  0 
D: 50,  24 
D: 51,  0 
D: 52,  24 
D: 53,  0 
L0: 4000, 160, 15joint encoder transferred 

My question is why is D:1 there? My understanding of unions and structures is that because command type is uint8_t, then it should only occupy one data space, and therefore UpperLimits[0] should start on D:1, but it seems that command_type is acting as a uint16_t and posting another bit. Why is this so?

Note: you may see that the index goes up to count data[53] which should be out of bounds, but I need to read this and send it so as to be able to deconstruct the data at the other end.

timrau
  • 22,578
  • 4
  • 51
  • 64
rocklegend
  • 81
  • 11

2 Answers2

0

There is almost certainly a padding byte between commandtype and UpperLimits; the 2-byte myint16_t data type is aligned on an even byte boundary.

struct {
    uint8_t commandtype;
    myint16_t UpperLimits[4];
    ...

If you could print the size of the anonymous structure and union, you're likely to find that it is at least 54 bytes (where you think it should be 53). One of the disadvantages of untagged structure types embedded as anonymous members of a union is that there is no easy way to print the size of the structure. Give the structure a tag (struct tag { uint8_t commandtype; ...) so that you can print its size out.

There isn't an easy fix for that using the current framework.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

You need to tell the compiler how to align the elements in your union or struct. Simply add #pragma pack(push,1) before your definitions to instruct the compiler to align adjacent elements on one-byte boundaries, and then use #pragma pack(pop) to revert to the compiler's default alignment. For performance reasons, you would not want to use one-byte alignment for all structs/unions/classes, so it's a good idea to wrap your union definition in the pragma pair.

Example:

#pragma pack(push,1)
union
{
uint_8 first;
uint_8 second;
} two_bytes;
#pragma pack(pop)

assert(sizeof two_bytes == 2);
Steger
  • 887
  • 7
  • 16