2017-08-16 115 views
0

我有一个ADXL355(EVAL-ADXL355-PMDZ),我试图用它来测试非常昂贵的工业级传感器。我正在使用I2C,并且能够读取设备属性和设置,如datasheet中所述。Arduino从3个寄存器中读取20位数字

我遇到的问题是如何读取3个ZDATA(或XDATA,YDATA)寄存器作为单个值。我尝试了两种方法。这是第一个:

double values[3]; 
Wire.beginTransmission(addr); 
    Wire.write(0x08); // ACCEL_XAXIS 
    Wire.endTransmission(); 
    Wire.requestFrom(addr, 9, true); // Read 9, 3 for each axis 
    byte x1, x2, x3; 
    for (int i = 0; i < 3; ++i){ 
    x3 = Wire.read(); 
    x2 = Wire.read(); 
    x1 = Wire.read(); 
    unsigned long tempV = 0; 
    unsigned long value = 0; 
    value = x3; 
    value <<= 12; 
    tempV = x2; 
    tempV <<= 4; 
    value |= tempV; 
    tempV = x1; 
    tempV >>= 4; 
    value |= tempV; 
    values[i] = SCALEFACTOR * value; 
    } 

这将产生接近1g的负重力和3g的正重力值。此外,未加载的轴有时会显示高档而不是-0.0克。它们从0.0克升到4.0克。这告诉我我有一个迹象问题,我肯定来自使用unsigned long。所以我试图把它看作一个16位的值并保留这个符号。

double values[3]; 
    Wire.beginTransmission(addr); 
    Wire.write(0x08); // ACCEL_XAXIS 
    Wire.endTransmission(); 
    Wire.requestFrom(addr, 9, true); // Read 9, 3 for each axis 
    byte x1, x2, x3; 
    for (int i = 0; i < 3; ++i){ 
    x3 = Wire.read(); 
    x2 = Wire.read(); 
    x1 = Wire.read(); 
    long tempV = 0; 
    long value = 0; 
    value = x3; 
    value <<= 8; 
    tempV = x2; 
    value |= tempV; 
    values[i] = SCALEFACTOR * value; 
    } 

这产生的值在符号上来讲不错,但他们(预期)的幅度远远低于他们应该是。我试图创建这样long value:20;一个20位的数字,但我以前

预计初始收到

“:”令牌

同样的错误了int

如何正确读取3个寄存器以获取正确的20位值?

回答

2

首先,当使用左右移位运算符(请参阅this question)时,您确实想要使用无符号类型。

纵观给我们得知long是在4个字节(即32位),所以它们是足够长的时间(没有双关语意)“持有”所代表的avr-gcc type layout您的20位数字(XDATA,YDATA和ZDATA) 。另一方面,int用2个字节表示(即16位),因此不应该用于你的情况。

根据您链接页面33的数据表,数字被格式化为two's complement。你的第一个例子正确地设置了最后20位你的无符号32位长value(特别是左对齐处理 - 右移x1四 - 看起来正确)但是“新”12位最高有效位是始终设置为0

要执行sign extension,你需要“新的” 12最显著位,如果数字是正值,1设置为0,如果数字是负值(你的第一个例子适应):

... 
value |= tempV; 
if (x3 & 0x80) /* msb is 1 so the number is a negative value */ 
    value |= 0xFFF00000; 

从那里,你应该观察到的是与以前相同的行为:高正值而不是小负值(但比以前更高)。这是因为虽然你的value是按位进行正确的说话,但仍然将解释为为无符号。这可以通过强制编译器使用value作为被签署合作周围:

values[i] = SCALEFACTOR * (long)value; 

现在它应该是工作。

请注意,此答案使用的事实是,您的C/C++实现使用二进制补码来表示负整数。虽然在实践中非常罕见,但该标准允许其他表示(例如参见this question)。

0

这是一种使其工作的方法。它对签名值使用偏移。各方都表示这是一个潜在的错误,因为它是实现定义的。它在我的平台上工作。

typedef union { 
    byte bytes[3]; 
    long value:24; 
} accelData; 

double values[3]; 
Wire.beginTransmission(addr); 
Wire.write(0x08); // ACCEL_XAXIS 
Wire.endTransmission(); 
Wire.requestFrom(addr, 9, true); // Read 9, 3 for each axis 
accelData raw; 
for (int i = 0; i < 3; ++i){ 
    raw.bytes[2] = Wire.read(); 
    raw.bytes[1] = Wire.read(); 
    raw.bytes[0] = Wire.read(); 
    long temp = raw.value >> 4; 
    values[i] = SCALEFACTOR * (double)temp; 
} 

我更喜欢Alexandre Perrin提出的解决方案。

+0

还要注意,这个实现依赖于字节顺序。 –

+0

@Alexandre Perrin你也是。这是不必要的坏评论 –

+0

@PeterJ据我所知,我的解决方案只使用按位操作,它的工作是一样的,而不管最终性如何(不像union union-punning)。看到[这个问题](https://stackoverflow.com/questions/1041554/bitwise-operators-and-endianness)。 –

0
union 
{ 
    struct 
    { 
     unsigned : 4; 
     unsigned long uvalue : 20; 
    }; 
    struct 
    { 
     unsigned : 4; 
     signed long ivalue : 20; 
    }; 
    unsigned char rawdata[3]; 
}raw; 

for (int i = 0; i < 3; ++i){ 
    raw.bytes[2] = Wire.read(); //if most significant part is transfered first 
    raw.bytes[1] = Wire.read(); 
    raw.bytes[0] = Wire.read(); 

    values[i] = SCALEFACTOR * (double)raw.ivalue; 
}