简体   繁体   English

我应该使用位域来映射传入的串行数据吗?

[英]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: 例如,我们有一个1.5字节的数据,因此我们使用了struct:

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

The reserved bits are always 1 保留位始终为1

So for example, if the incoming data is 0xD2,0xF4, the value is 0x04D2, or 1234. 因此,例如,如果传入数据为0xD2,0xF4,则值为0x04D2或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)? 不论字节序如何, data1都会始终按预期表示正确的值吗(我想是的,并且硬件/软件接口应始终对单个完整字节正确处理该值-如果发送了0xD2,则应接收到0xD2)?

  • Could data2 and reserved be the wrong way around, with data2 representing the upper 4 bits instead of the lower 4 bits? 可能data2reserved是错误的方法, data2代表高4位而不是低4位?

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? 看来Intel上的所有linux系统都是相同的-ARM也是这样吗? (If we can say we can support all Intel and ARM linux builds, we should be OK) (如果可以说我们可以支持所有英特尔和ARM linux构建,那应该没问题)

  • 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. 我们之所以不首先执行此操作,是因为许多数据字段小于1个字节,而不是大于1个字节-这意味着通常对于位字段,我们不必进行任何屏蔽和移动,因此后处理更加简单。

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. 无论字节顺序如何,data1始终将按预期表示正确的值。

Yes, but that is because uint8_t is smallest possible addressable unit: a byte. 是的,但这是因为uint8_t是最小的可寻址单元:一个字节。 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? 可能data2和reserved是错误的方法,data2代表高4位而不是低4位?

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. 同样,编译器不必为位域支持uint8_t ,即使它另外支持该类型。

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. 最低有效位将始终位于最低有效字节中,但是无法确定在C 该位将在字节中的何处

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. 移位运算符给出了足够好的顺序的可靠抽象:对于数据类型uint8_t ,对于所有编译器和所有体系结构, (1u << 0)始终是最低有效位,而(1u << 7)是最高有效位。

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. 您可以为基本数据类型编写一个简单的可重用转换函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM