c - 如何将两个寄存器的 2 的补码值读入一个 int

标签 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

这是数据表: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);
}

我做错了什么?这不是我应该如何将 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);
}

解决评论中的问题:

如果没有错误,i2c_smbus_read_word_data() 函数将从 i2c 总线获取的 16 位数据作为无符号 16 位值返回。函数返回的 32 位 int 可以很容易地表示 16 位无符号值,因此根据定义,16 位数据不能为负数。当且仅当出现错误时,res 才会为负。

将 16 位值解释为(可能为负的)二进制补码值由 res(__s16) 转换处理。这会获取 res 中的值并将其转换为带符号的 16 位 int 表示形式。严格来说,它是关于此转换如何处理负数的实现定义的。我相信在 Linux 实现中,这将始终简单地将 res 的低 16 位视为二进制补码。

如果您担心 (__s16) 转换的实现定义方面,您可以通过使用算术而不是 caf 的答案中的转换来避免它:

temp_value = (res > 0x7fff) ? res - (0xffff + 1) : res;

即使您碰巧在一个补码机器上运行,它也会执行正确的负值转换(Linux 甚至支持在这样的机器上运行吗?)。

另请注意,上面发布的代码假定您在小端机器上运行 - 在将数据转换为负值之前,您需要在大端机器上适本地交换字节,应该执行以下操作然而, objective-c PU 表示整数值(大/小,一个或两个)的技巧:

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

关于c - 如何将两个寄存器的 2 的补码值读入一个 int,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11920411/

相关文章:

c++ - C2061 : syntax error : identifier '_TCHAR'

cpu-registers - bochsdbg中CPU寄存器FLAG如何理解和修改?

pointers - 在方括号中添加两个寄存器是什么意思?

assembly - 如何将AVX ymm寄存器中的所有值设置为相同(均为0/1/特定值)?

c - 如何在各种编码中统一检测整数的符号位(1's complement, 2' 的补码,符号大小)?

c++ - 在您的 dll 项目中包含外部 DLL 依赖项吗?

C 自动变量

c++ - lw 在 C 或 C++ 中是如何表示的?

c++ - 减去未知 bitesize 的两个值

c++ - 添加两个有符号或无符号整数