我有一个16位变量data
,即:
volatile uint16_t data;
我需要根据外部传感器上两个8位寄存器的内容填充此值。这些通过I2C / TWI访问。
我的TWI例程是异步*,并且具有签名:
bool twi_read_register(uint8_t sla, uint8_t reg, uint8_t *data, void (*callback)(void));
这会将reg
上sla
的值读入*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);
但是,我不喜欢将endian依赖到我的代码中。有没有办法以与端元无关的方式实现这一目标?
(旁注:我目前的实际解决方法是使用结构,即:
typedef struct {
uint8_t msb;
uint8_t lsb;
} SensorReading;
但我很好奇我是否可以用简单的uint16_t
)
修改
(* by async我的意思是分裂阶段,即*data
将在未来的某个时间点设置,此时如果请求,将通过callback
函数通知被调用者。
答案 0 :(得分:4)
以下不起作用吗?
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*)
使用指针转换以便携方式执行此操作。
请注意,上面的内容可能会以可移植的方式隐藏字节序,结构打包也可能因目标而异,并且可能仍会在某个目标上中断。对于上面的例子,这是不太可能的,但是有一些奇怪的目标。我想说的是,在这个设备级别上编程可能是不可能的,并且最好接受它并努力隐藏紧凑目标接口中的所有细节,因此更改目标的一个头文件足以支持它。然后,代码的其余部分可以看起来与目标无关。
答案 1 :(得分:0)
这样的事情怎么样?
uint16_t myValue = ...;
uint8_t LSB = (uint8_t)(myValue % 256);
uint8_t MSB = (uint8_t)(myValue / 256);
答案 2 :(得分:0)
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);