Guaranteeing bit endianness of structs for serial data transmission (SPI)

问题描述 投票:0回答:1

我正在尝试通过串行外设接口 (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 驱动程序只接受一种解决方案,即先发送最高有效字节,同时也先发送最高有效位。这是我的想法:

现在我的问题:

  • htonl 和 htons 是否也翻转 bit 顺序?
  • 在现实生活中真的存在像 big endian with LSbit first 这样的东西吗?还是 byte 排序总是表示 bit 排序?
  • 我如何保证跨平台,所讨论的数据结构始终完全正确地放置在位缓冲区中?
c++ struct endianness spi memory-layout
1个回答
0
投票

在大多数(所有?)处理器中,位排序并不是真正的事情,因为内存是以字节为单位寻址的。看起来好像所有的位都彼此相邻,并且字节堆叠在彼此之上。

例如:

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;
}

虽然你很可能会摆脱结构包装。

© www.soinside.com 2019 - 2024. All rights reserved.