Arduino 从 3 个寄存器中读取 20 位数字



我有一个ADXL355(EVAL-ADXL355-PMDZ(,我正在尝试用非常昂贵的工业级传感器进行测试。 我正在使用I2C,能够读取数据表中描述的器件属性和设置。

我遇到的问题是如何将 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;
}

这个产生的值在符号方面是好的,但它们(如预期的那样(比它们应该的要低得多。 我试图创建一个像这样的 20 位数字long value:20;但我收到了

":"标记之前的预期初始值设定项

int相同的错误。

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

首先,在使用左右移位运算符时,您确实希望使用无符号类型(请参阅此问题(。

看看 avr-gcc 类型的布局,我们了解到long用 4 个字节(即 32 位(表示,因此它们足够长(没有双关语(来"保存"您的 20 位数字(XDATA、YDATA 和 ZDATA(。另一方面,int以 2 个字节(即 16 位(表示,因此不应在您的情况下使用。

根据您链接第 33 页的数据表,数字的格式为 2 的补码。您的第一个示例正确设置了无符号、32 位长value的最后20 位(特别是左对齐处理 - 右移x14 - 看起来已经正确(,"新"的 12 个最重要的位始终设置为0

要执行符号扩展,您需要将"新"12 个最高有效位设置为0数字是否为正值,1数字是否为负值(改编您的第一个示例(:

...
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++ 实现使用 two 的补码来表示负整数。虽然在实践中很少见,但该标准允许其他表示(有关示例,请参阅此问题(。

这是使其工作的一种方法。 它确实对有符号值使用位移。 各种消息来源都说这是一个潜在的错误,因为它是实现定义的。 它在我的平台上工作。

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

我更喜欢亚历山大·佩林提出的解决方案。

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

最新更新