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

时间:2017-08-16 21:00:09

标签: arduino

我有一台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位值?

3 个答案:

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