Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
330 views
in Technique[技术] by (71.8m points)

c - Should I use bit-fields for mapping incoming serial data?

We have data coming in over serial (Bluetooth), which maps to a particular structure. Some parts of the structure are sub-byte size, so the "obvious" solution is to map the incoming data to a bit-field. What I can't work out is whether the bit-endianness of the machine or compiler will affect it (which is difficult to test), and whether I should just abandon the bit-fields altogether.

For example, we have a piece of data which is 1.5 bytes, so we used the struct:

{
    uint8_t data1; // lsb
    uint8_t data2:4; // msb
    uint8_t reserved:4;
} Data;

The reserved bits are always 1

So for example, if the incoming data is 0xD2,0xF4, the value is 0x04D2, or 1234.

The struct we have used is always working on the systems we have tested on, but we need it to be as portable as possible.

My questions are:

  • Will data1 always represent the correct value as expected regardless of endianness (I assume yes, and that the hardware/software interface should always handle that correctly for a single, whole byte - if 0xD2 is sent, 0xD2 should be received)?

  • Could data2 and reserved be the wrong way around, with data2 representing the upper 4 bits instead of the lower 4 bits?

If yes:

  • Is the bit endianness (generally) dependent on the byte endianness, or can they differ entirely?

  • Is the bit-endianness determined by the hardware or the compiler? It seems all linux systems on Intel are the same - is that true for ARM as well? (If we can say we can support all Intel and ARM linux builds, we should be OK)

  • Is there a simple way to determine in the compiler which way around it is, and reserve the bit-fields entries if needed?

Although bit-fields are the neatest way, code-wise, to map the incoming data, I suppose I am just wondering if it's a lot safer to just abandon them, and use something like:

struct {
    uint8_t data1; // lsb (0xFF)
    uint8_t data2; // msb (0x0F) & reserved (0xF0)
} Data;

Data d;

int value = (d.data2 & 0x0F) << 16 + d.data1

The reason we have not just done this in the first place is because a number of the data fields are less than 1 byte, rather than more than 1 - meaning that generally with a bit-field we don't have to do any masking and shifting, so the post-processing is simpler.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Should I use bit-fields for mapping incoming serial data?

No. Bit-fields have a lot of implementation specified behaviour that makes using them a nightmare.

Will data1 always represent the correct value as expected regardless of endianness.

Yes, but that is because uint8_t is smallest possible addressable unit: a byte. For larger data types you need to take care of the byte endianness.

Could data2 and reserved be the wrong way around, with data2 representing the upper 4 bits instead of the lower 4 bits?

Yes. They could also be on different bytes. Also, compiler doesn't have to support uint8_t for bitfields, even if it would support the type otherwise.

Is the bit endianness (generally) dependent on the byte endianness, or can they differ entirely?

The least signifact bit will always be in the least significant byte, but it's impossible to determine in C where in the byte the bit will be.

Bit shifting operators give reliable abstraction of the order that is good enough: For data type uint8_t the (1u << 0) is always the least significant and (1u << 7) the most significant bit, for all compilers and for all architectures.

Bit-fields on the other hand are so poorly defined that you cannot determine the order of bits by the order of your defined fields.

Is the bit-endianness determined by the hardware or the compiler?

Compiler dictates how datatypes map to actual bits, but hardware heavily influences it. For bit-fields, two different compilers for the same hardware can put fields in different order.

Is there a simple way to determine in the compiler which way around it is, and reserve the bit-fields entries if needed?

Not really. It depends on your compiler how to do it, if it's possible at all.

Although bit-fields are the neatest way, code-wise, to map the incoming data, I suppose I am just wondering if it's a lot safer to just abandon them, and use something like:

Definitely abandon bit-fields, but I would also recommend abandoning structures altogether for this purpose, because:

  • You need to use compiler extensions or manual work to handle byte order.

  • You need to use compiler extensions to disable padding to avoid gaps due to alignment restrictions. This affects member access performance on some systems.

  • You cannot have variable width or optional fields.

  • It's very easy to have strict aliasing violations if you are unaware of those issues. If you define byte array for the data frame and cast that to pointer to structure and then dereference that, you have problems in many cases.

Instead I recommend doing it manually. Define byte array and then write each field into it manually by breaking them apart using bit shifting and masking when necessary. You can write a simple reusable conversion functions for the basic data types.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...