我正在尝试通过串行外设接口 (SPI) 发送数据,该接口需要一个缓冲区并首先将其 MSbit 发送到数据输出。为了成功,我需要以完全正确的方式将我的结构转换为位缓冲区,我不确定如何保证 cross platform 它始终有效。
这是我要发送的结构:
using data_settings = uint8_t;
enum class channel_settings : uint8_t
{
power_down_setting = 0,
DA1,
DA2,
DA3,
DA4,
DA5,
DA6,
DA7,
DA8,
power_down_release,
NA1,
NA2,
io_da_select,
io_serial_parallel,
io_parallel_serial,
io_status_setting,
};
struct message // send this over SPI
{
data_settings data;
channel_settings channel;
};
如您所见,结构消息的大小为 2 个字节,我想它在大端/小端系统上的布局会有所不同。但是 bit 顺序呢?从理论上讲,无论字节字节顺序如何,这些位也可以以任何一种方式布置,或者不能吗?但是 SPI 驱动程序只接受一种解决方案,即先发送最高有效字节,同时也先发送最高有效位。这是我的想法:
现在我的问题:
在大多数(所有?)处理器中,位排序并不是真正的事情,因为内存是以字节为单位寻址的。看起来好像所有的位都彼此相邻,并且字节堆叠在彼此之上。
例如:
uint8_t c = 13;
bool lsBit = c & 0x01;
lsBit 始终是最低有效位。它在寄存器中的物理存储位置并不重要。因为我们不能像
c[n]
那样解决它以获得第 n 位。c << 1
始终与
c*2
相同。
当你这样做时,字节序开始发挥作用:
uint32_t someValue = 123;
uint8_t* smallptr = (uint8_t*) &someValue;
*smallptr == ???
现在我们取一个 32 位值的地址,并将其解释为 8 位值。所以现在我们正在处理地址,因为有 4 个地址指向
someValue
的 4 个字节,而 smallptr 指向哪个,取决于您的字节顺序。
也就是说,在电线上有一个命令。因此,外围设备(或者驱动程序,如果你是位碰撞)有责任按正确的顺序放置这些位。大多数协议实际上定义了哪一位先行。但是维基百科关于 spi 是这样说的:
数据通常先移出最高有效位。
这似乎没有完全强制执行。
结构不强制字节排序/打包。所以不保证
sizeof(message)==2
。或者顺序保持不变(尽管这很有可能)。
hacky 的方法是使用
packed
属性。但是你可能会有其他问题:gcc 的 __attribute__((packed)) / #pragma pack 不安全吗?
我认为最合适的方法是编写如下函数:
message::serialize(uint8_t* dest){
dest[0] = data;
dest[1] = channel;
}
虽然你很可能会摆脱结构包装。