Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Complicated struct/union

Status
Not open for further replies.

easyts

Programmer
Jan 31, 2007
8
ES
Hi,

I'm writing a code and I have a problem with structs and unions.

I read 8 bytes (RXBnD[8]) from an external system, and each of these 8 bytes are structured in 8 bits:

********************************
__saddr union aux_bits2
{
struct
{unsigned char b0:1;
unsigned char b1:1;
unsigned char b2:1;
unsigned char b3:1;
unsigned char b4:1;
unsigned char b5:1;
unsigned char b6:1;
unsigned char b7:1;
};
unsigned char byte;
} RXBnD[8];
***********************************

The problem is that the 'speed' variable is in byte RXBnD[1] and bits 3...0 of byte RXBnD[0]. Other variables are also 'mixed' in various bytes.

So, what I want is to make a union or struct or anything else so that I get the value of 'speed' variable without reading byte RXBnD[1] and bits 3...0 of byte RXBnD[0]. Do you understand me?


Thanks.

 
Bit fields are not portable - you should not use them for pulling apart external data formats.

Code:
typedef struct {
  int speed;
} rx_t;
unsigned char RXBnD[8];

void unpack ( const unsigned char *rxbnd, rx_t *data ) {
  data->speed = (rxbnd[1] << 8) | rxbnd[0] & 0x0F;
}
Whilst it seems like hard work, bear in mind that
- it will always work irrespective of your platform
- it will even work if the bits in your structure are discontinuous.


--
If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
 
From the description, I can't quite figure out whether bits 0..3 the first or last 4 bits. Also, are bits 4..7 of speed blank?
Code:
/* bits 0..3 MS */
data->speed = (rxbnd[1] << 4) | (rxbdn[0] >> 4);

/* bits 0..3 LS */
data->speed = (rxbnd[1] << 4) | (rxbdn[0] & 0x0F);
 
Thanks for your reply.

What you've done it's not exactly what I want .This code works correctly but is not useful because I receive 4 kinds of messages, and each of them have 8 bytes, where the variables can be 1 byte or only some bits (as the 'seed' variable I showe you before). So if I have to read all the messages, it can be too long for getting all the variables.

So I thought that there could be a way (with structures and unions) to get instantly the value of 'speed' by only writing to the array RXBnD[8]. How can I do a union of the byte RXBnD[1] and the 4 lowest bits of RXBnD[0]? I know how to do a union of two bytes, bits or anything else, but not of two 'variables' already declared.

I hope you understand me...
 
Well, I've tried to solve it and it almost works correctly, but not all.

*********************************

__saddr union mess0x100
{
struct
{unsigned char sensor:4;
unsigned short speed:12;
unsigned char res0:8;
unsigned short rpm:16;
unsigned char res1:1;
unsigned char avaria:1;
unsigned char res2:4;
unsigned char cavallet:1;
unsigned short res3:16;
unsigned char res4:1;
};
unsigned char byte[8];
} RXBnD100;


*****************************

When I run this code, I write each byte of the array RXBnD100 with:

RXBnD100.byte[0]=0x01;
RXBnD100.byte[1]=0xAA;
RXBnD100.byte[2]=0xBB;
RXBnD100.byte[3]=0x22;
RXBnD100.byte[4]=0x33;
RXBnD100.byte[5]=0x44;
RXBnD100.byte[6]=0x55;
RXBnD100.byte[7]=0x66;

So, when I read RXBnD100.speed, it must show 0x1AA but I don't get it. RXBnD100.byte[0..7] are well written, so I don't understand where is the problem.
 
[tt]--- Byte 1 --- --- Byte 0 ---
[blue]7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0[/blue]
[red]S S S S S S S S x x x x S S S S[/red]
1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 1[/tt]

You want
[tt]unsigned char sensor:4;
unsigned short speed:12;[/tt]
Now assuming for the moment that your compiler allocates bit fields from MSB to LSB (there's no guarantee of that), so you end up with bits 7 to 4 of byte 0 (the 'x' bits) mapped to sensor, and all the 'S' bits mapped to speed (but see endian below).

If it allocates from LSB to MSB, then you'll end up with 1 stored in sensor (the 'S' bits of byte 0), and the speed will be composed of the 'S' bits from byte 1, and the 'x' bits from byte 0.
Result either 0xAA0 or 0x0AA depending on endian (see next paragraph).

Now if your machine is little endian (as it seems to be), then the 'S' bits on byte 0 are going to be at the bottom of the short int, not the top.
The result being that you end up with speed being 0xAA1.

If your machine is the wrong endian with respect to the data, then I don't think any amount of bit-field magic will work for you.

Like I said, bit fields and external data formats do not mix well. There are way too many system details which cannot be overcome easily.

Bit bashing with & | << and >> may seem tedious, but it's the only way you'll get a guaranteed answer with any kind of repeatability.
The up-side is that if you refer to these fields often, then the code will be more efficient in the long run.

Even if you do happen to find something which works for your compiler, it could all come undone again when you update / patch your compiler or move to another compiler.


--
If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
 
You also have to make sure that the compiler has the 'packed' option switched on. A declaration like

unsigned char sensor:4;
unsigned short speed:12;
unsigned char res0:8;
unsigned short rpm:16;

does not always guarantee that the data will fall on nice boundaries which are convenient for the compiler.

If you have a 16-bit architecture, you may find that the compiler has inserted a dummy byte either before or afer res0 so that rpm falls on a 16 bit boundary.

No idea what will happen on a 32 bit architecture.

Also, if you have the packed option switched on, do not use the variables in arithmetic. Assign them to something else first and then use the something else. Most machines will give up on arithmetic when the data lies across word boundaries.
 
Well, I'm using a NEC uP of 8 bits, and the compiler is the IAR Embedded Workbench.

Salem, you're right. I'm having problems because of the compiler is allocating bits from LSB to MSB when I need MSB to LSB.

xwb, I'll be aware of the 'packed' option.


Thanks, I'll let you know how it ends...
 
Hi,

I finally could solve the problem... This is the code I use.

__saddr union mess0x100
{
struct
{
unsigned short rpm:16;
unsigned short res3:16;
union speed_sensor1
{ unsigned short speed:12;
struct
{
unsigned char dummy;
unsigned char dummy2:4;
unsigned char sensor:4;
};
} vel_sensor;
unsigned char res0:8;
unsigned char res1:1;
unsigned char avaria:1;
unsigned char res2:4;
unsigned char cavallet:1;
unsigned char res4:1;
};
unsigned char byte[8];
} RXBnD100;
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top