我有一台ADXL355(EVAL-ADXL355-PMDZ),我试图用非常昂贵的工业级传感器进行测试。我正在使用I2C,我能够按照datasheet。
中的描述读取设备属性和设置我遇到的问题是如何将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.0g。它们从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位值?
答案 0 :(得分:2)
首先,在使用左移和右移运算符时,您确实希望使用无符号类型(请参阅this question)。
看一下avr-gcc type layout,我们知道long
代表4个字节(即32位),所以它们足够长(没有双关语意)到&#34; hold&#34;您的20位数字(XDATA,YDATA和ZDATA)。另一方面,int
以2个字节(即16位)表示,因此不应在您的情况下使用。
根据您链接到第33页的数据表,这些数字的格式为two's complement。您的第一个示例正确设置无符号,32位长value
的最后20位(特别是左对齐处理 - 右移x1
四 - 已经看起来正确)但&#34; new&#34; 12个最高有效位始终设置为0
。
要执行sign extension,您需要设置&#34; new&#34;如果数字为正值,则为0
的12个最高有效位,如果数字为负值则为1
(适用于您的第一个示例):
...
value |= tempV;
if (x3 & 0x80) /* msb is 1 so the number is a negative value */
value |= 0xFFF00000;
从那里开始,您应该观察到与以前相同的行为:高正值而不是小负值(但甚至高于先前的值)。这是因为虽然你的value
正确地按位说话,但它仍然是解释为无符号。这可以通过强制编译器使用value
作为signed:
values[i] = SCALEFACTOR * (long)value;
现在应该可以了。
请注意,这个答案使用的事实是您的C / C ++实现使用两个补码来表示负整数。虽然在实践中非常罕见,但该标准允许其他表示形式(例如,请参阅this question)。
答案 1 :(得分: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提出的解决方案。
答案 2 :(得分: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;
}