Ret*_*eto 4 c cpu-registers twos-complement
我试图从 STC3100 电池监视器 IC 读取值,但我得到的值不正确。数据表说的是:
The temperature value is coded in 2’s complement format, and the LSB value is 0.125° C.
REG_TEMPERATURE_LOW, address 10, temperature value, bits 0-7
REG_TEMPERATURE_HIGH, address 11, temperature value, bits 8-15
Run Code Online (Sandbox Code Playgroud)
这是数据表:http : //www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00219947.pdf
我的代码中有什么:
__u8 regaddr = 0x0a; /* Device register to access */
__s32 res_l, res_h;
int temp_value;
float temperature;
res_l = i2c_smbus_read_word_data(myfile, regaddr);
regaddr++;
res_h = i2c_smbus_read_word_data(myfile, regaddr);
if (res_l < 0) {
/* ERROR HANDLING: i2c transaction failed */
} else {
temp_value = (res_h << 8)+res_l;
temperature = (float)temp_value * 0.125;
printf("Temperature: %4.2f C\n", temperature);
}
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?这不是我应该如何将 2 的补码值复制到 int 中吗?
i2c_smbus_read_word_data()将从设备上的指定寄存器开始读取 16 位,因此一个i2c_smbus_read_word_data()将读取您感兴趣的两个寄存器,使用单个 i2c 事务。
i2c_smbus_read_word_data()返回从设备读取的 16 位作为无符号数量 - 如果出现错误,则返回i2c_smbus_read_word_data()值为负。您应该能够像这样读取温度传感器:
__u8 regaddr = 0x0a; /* Device register to access */
__s32 res;
int temp_value;
float temperature;
res = i2c_smbus_read_word_data(myfile, regaddr);
if (res < 0) {
/* ERROR HANDLING: i2c transaction failed */
} else {
temp_value = (__s16) res;
temperature = (float)temp_value * 0.125;
printf("Temperature: %4.2f C\n", temperature);
}
Run Code Online (Sandbox Code Playgroud)
要解决评论中的问题:
i2c_smbus_read_word_data()如果没有错误,该函数将从 i2c 总线获得的 16 位数据作为无符号的 16 位值返回。16 位无符号值可以很容易地用函数返回的 32 位 int 表示,因此根据定义,16 位数据不能为负。res当且仅当出现错误时才会为负数。
解读16位值作为(可能为负)补值由处理(__s16)的铸造res。这将获取其中的值res并将其转换为有符号的 16 位int表示。严格来说,它是关于此强制转换将如何处理负数的实现定义的。我相信在 Linux 实现中,这将始终简单地将 的低 16 位res视为二进制补码数。
如果您担心强制转换的实现定义方面(__s16),您可以通过使用算术而不是 caf 的答案中的强制转换来避免它:
temp_value = (res > 0x7fff) ? res - (0xffff + 1) : res;
Run Code Online (Sandbox Code Playgroud)
即使您碰巧在一个补码机器上运行(Linux 甚至支持在这样的机器上运行?),哪个将执行正确的转换为负值。
另请注意,上面发布的代码假设您在小端机器上运行 - 在将数据转换为负值之前,您需要在大端机器上适当交换字节,但是以下应该可以解决问题目标 CPU 表示整数值(大/小,一或二):
__u16 data = __le16_to_cpu( (__u16) res);
// convert negative two's complement values to native negative value:
int temp_value = (data > 0x7fff) ? data - (0xffff + 1) : data;
Run Code Online (Sandbox Code Playgroud)