将二进制补码,左对齐整数转换为常规二进制

时间:2018-03-24 17:16:27

标签: binary type-conversion raspberry-pi3

我正在使用C ++和Qt编写Raspberry Pi 3。我正在使用wiringPi库与I2C加速度计(我用来比较倾斜角度值但不测量角度度)进行接口。

我需要确定一个加速度计读数是大于还是小于前一个或具有任意预设值。 (我在机器上有一个手臂,我想将其设置为任意角度,然后让手臂移动到该角度。)

加速度计以两个字节的二进制补码数据输出读数。数据是左对齐的十二位。 (我假设这意味着加速度计值的4个最高有效位在高位字节的左侧?)加速度计是LIS3DH。

我正在将两个字节作为2个整数值读入我的程序,但我正在努力将数据转换为有用的信息。

wiringPi I2C库仅返回整数。

我可能需要将十进制读数转换为二进制,将两个字节的二进制补码左对齐二进制数据转换为十二位右对齐的常规二进制数据。

我正在寻找关于如何实现这一目标的建议,以及是否有比我上面列出的路径更容易的方法。

编辑:

void main(void)       
{
    int acc_l = 0, acc_h = 0;
    acc_l = read_output_register_lo;
    acc_h = read_output_register_hi;

/*
How do I convert acc_l and acc_h to binary?

Do I need to shift acc_h 4 bits to the right?

How do I concatenate acc_h and acc_l into a 12 bit binary number?

How do I convert 12 bit twos compliment binary into regular binary?

Is this the correct process to follow?

Is there an easier way to do this?
*/
}

编辑: 非常感谢Roman。我提出了相同的基本概念,但似乎我将两个寄存器颠倒了。再次感谢你。

2 个答案:

答案 0 :(得分:1)

我也在使用LIS3DH,经过几个小时的阅读和尝试后,我终于弄明白了。虽然我的代码是用于AVR微控制器,但方法仍然是相同的。

加速度计将读数显示为左对齐 16位值,每个轴分为2字节寄存器:OUT_X_LOUT_X_H,{{1 },OUT_Y_LOUT_Y_HOUT_Z_L。左对齐意味着:所有值位都向左移位。多少位?这取决于你的精确度。

假设您使用10位精度(默认值),并且想要读取X轴值,那么OUT_Z_HOUT_X_H寄存器将如下所示:

OUT_X_L

x - 是您想要的值,0只是零。由于精度为10位,因此6个不太重要的位将始终为零。

为什么有人会想要发送这样的数据?原因很简单 - 如果您不关心精度,则只能读取高位寄存器并将其存储为8位整数(╔═════════╦═══════════╦═══════════╗ ║ ║ OUT_X_H ║ OUT_X_L ║ ╠═════════╬═══════════╬═══════════╣ ║ Content ║ xxxx xxxx ║ xx00 0000 ║ ╚═════════╩═══════════╩═══════════╝ )。由于左对齐,保留了更多最高有效位,并且您从低位寄存器中仅丢失了2位。另一方面,右对齐更容易操作,但如果不读取整个16位值,则会丢失大量信息。

现在,如果要将此值转换为“正常”值(右对齐),则需要将其存储为16位整数(int8_t)。计算机将有符号整数作为二进制补码,LIS3DH也是如此,这意味着存储在此int16_t变量中的值将具有正确的符号。您需要做的最后一个操作是将此值右移6(对于10位精度)和 voila

我写过两种转换方法,一种是基于典型的位操作,另一种是指针式自杀。

位操作

int16_t

对于每个轴:

  1. 在16位变量的上半部分存储一个高位寄存器。
  2. 附加一个较低的注册表。现在,您的变量包含左对齐值。
  3. 将整个变量向右移动6个。
  4. 指针式自杀

    int16_t x = out_x_h; // 1.
    x <<= 8;             // 1.
    x |= out_x_l;        // 2.
    x >>= 6;             // 3.
    
    int16_t y = out_y_h;
    y <<= 8;
    y |= out_y_l;
    y >>= 6;
    
    int16_t z = out_z_h;
    z <<= 8;
    z |= out_z_l;
    z >>= 6;
    

    对于每个轴:

    1. 将寄存器存储在一个字节数组中。 元素顺序仅对小端架构有效。对于big-endian,高位寄存器应该是第一个。
    2. uint8_t x_a[2] = {out_x_l, out_x_h}; // 1. int16_t x = (*(int16_t*) x_a) >> 6; // 2. uint8_t y_a[2] = {out_y_l, out_y_h}; int16_t y = (*(int16_t*) y_a) >> 6; uint8_t z_a[2] = {out_z_l, out_z_h}; int16_t z = (*(int16_t*) z_a) >> 6; 指针强制转换为uint8_t指针,因此指向字节数组的指针被视为指向一个16位整数的指针,取消引用它,并向右移动6。

答案 1 :(得分:0)

根据LIS3DH(ADC有10位)的描述,您应该执行以下操作:

unsigned int result = ((acc_h & 0x03)<<8) + (acc_l & 0xFF);

对于12位,你需要下一个代码:

unsigned int result = ((acc_h & 0x0F)<<8) + (acc_l & 0xFF);