BTW, I've been coding for far too long, and still the union caught me with my pants down the other day.
I knew what I wanted to achieve, but still made minced meat of it.
The secret is each line in the union specifies a element, the largest element determines the end size of the union (space used in memory).
Other elements will occupy the same memory space, but be interpreted according to their type, ie a bit field in your application.
Other tricks could be
union {
int AsInt;
float AsFloat;
}
This allows you to write a float value, then grab the byte form equivalent for transfer/storage.
When digging down to byte level though you need to be careful of the endian order of your processor as to where the LSB lives in memory for a 16 or 32 bit value.
My personal fave is
union {
int Word;
struct {
char LSB;
char MSB;
} bytes;
}
This is used to rip a 16 bit value into the HIWORD/LOWORD far more efficiently than the typical mask and shift operations used.
Once again, take care with the endianess of your processor.