C语言 你能以一种独立于端部的方式得到一个指向低字节的指针吗?



我有一个16位变量data,即:

volatile uint16_t data;

我需要根据外部传感器上两个8位寄存器的内容填充此值。这些是通过I2C/TWI访问的。

我的TWI例程是async*,签名是:

bool twi_read_register(uint8_t sla, uint8_t reg, uint8_t *data, void (*callback)(void));

这将sla上的reg的值读入*data,然后调用callback()

如果我知道uint16_t在内存中的排列方式,比如MSB LSB,那么我可以:

twi_read_register(SLA, REG_MSB, (uint8_t *)&data, NULL);
twi_read_register(SLA, REG_LSB, (uint8_t *)&data + 1, NULL);

但是,我不喜欢在我的代码中添加端序依赖。有没有一种独立于端部的方式来实现这一点?

(旁注:我目前的实际解决方案涉及使用结构体,即:

)
typedef struct {
    uint8_t msb;
    uint8_t lsb;
} SensorReading;

但我很好奇我是否可以用一个简单的uint16_t)

编辑

(*异步我指的是分阶段,即*data将在将来的某个时候设置,此时如果请求,将通过callback函数通知被调用者)

下面的操作不可行吗?

uint8_t v1, v2;
twi_read_register(SLA, REG_MSB, &v1, NULL);
twi_read_register(SLA, REG_LSB, &v2, NULL);
data = ((uint16_t)v1<<8)|v2;

或者data如此易失性以至于twi_read_register需要写它。在这种情况下,我认为你必须使用依赖于端序的代码。

正如您在下面指出的那样,data确实是易失性的,因为还有另一个设备正在读取它。因此,在两个端序不同的设备之间建立了一个内存映射连接。这意味着您必须使用依赖于端序的代码。

你提到结构体是一种变通方法,但这是一种处理这个问题的标准方法。

#ifdef BIGENDIAN
typedef struct
{       uint8_t  msb, lsb;
} uint16_as_uint8_t;
#else
typedef struct
{       uint8_t  lsb, msb;
} uint16_as_uint8_t;
#endif

在上面可以加上union

union
{       uint16_as_uint8_t  as8;
        uint16_t           as16;
};

请注意,后者违反了C89标准,因为您的明确意图是写入union的一个字段并从另一个字段读取,这将导致未指定的值。从C99开始(幸运的是)支持这一点。在C89中,可以通过(char*)使用指针转换,以一种可移植的方式完成此操作。

请注意,上面可能看起来以一种可移植的方式隐藏了端序,结构包装也可能因目标而异,并且仍然可能在某些目标上中断。对于上面的例子来说,这不太可能,但是周围有一些奇怪的目标。我想说的是,在这个设备级别上进行可移植编程可能是不可能的,最好接受这一点,并努力将所有细节隐藏在紧凑的目标接口中,因此为目标更改一个头文件就足以支持它。然后代码的其余部分可以看起来与目标无关。

这样怎么样?

uint16_t myValue = ...;
uint8_t LSB = (uint8_t)(myValue % 256);
uint8_t MSB = (uint8_t)(myValue / 256);
  volatile uint16_t data;  
  uint16_t v = 1;
  twi_read_register(SLA, REG_MSB, (uint8_t *)&data + *((uint8_t *)&v), NULL);
  twi_read_register(SLA, REG_LSB, (uint8_t *)&data + *((uint8_t *)&v + 1), NULL);

相关内容

  • 没有找到相关文章