2017-08-16 77 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; 
}