2020年1月6日月曜日

AE-BMX055の加速度センサーだけ使ってみた。

AE-BMX055とESP-WROOM-02の接続の続き。
ですが、、、加速度センサのみに絞っての確認となります。

前回のスケッチから加速度センサの箇所のみを抜粋。
BMX055_ACCELEROMETER
#include <Wire.h>
// BMX055 加速度センサのI2Cアドレス  
#define Addr_Accl 0x18  // (JP1,JP2,JP3 = Closeの時)

// センサーの値を保存するグローバル関数
float xAccl = 0.00;
float yAccl = 0.00;
float zAccl = 0.00;

void setup()
{
  // Wire(Arduino-I2C)の初期化
  Wire.begin();
  // デバック用シリアル通信は9600bps
  Serial.begin(9600);
  //BMX055 初期化
  //------------------------------------------------------------//
  Wire.beginTransmission(Addr_Accl);
  Wire.write(0x0F);  // PMU_Rangeレジスタを選択
  Wire.write(0x03);  // Range = +/- 2g
  Wire.endTransmission();
  delay(100);
  //------------------------------------------------------------//
  Wire.beginTransmission(Addr_Accl);
  Wire.write(0x10);  // PMU_BWレジスタを選択
  Wire.write(0x08);  // Bandwidth = 7.81 Hz
  Wire.endTransmission();
  delay(100);
  //------------------------------------------------------------//
  Wire.beginTransmission(Addr_Accl);
  Wire.write(0x11);  // PMU_LPWレジスタを選択
  Wire.write(0x00);  // Normal mode, Sleep duration = 0.5ms
  Wire.endTransmission();
  delay(100);
}

void loop()
{
  Serial.println("--------------------------------------"); 

  //BMX055 加速度の読み取り
  int data[6];
  for (int i = 0; i < 6; i++)
  {
    Wire.beginTransmission(Addr_Accl);
    Wire.write((2 + i));// データレジスタを選択
    Wire.endTransmission();
    Wire.requestFrom(Addr_Accl, 1);// 1バイトのデータを要求する
    // 6バイトのデータを読み取る
    // xAccl lsb, xAccl msb, yAccl lsb, yAccl msb, zAccl lsb, zAccl msb
    if (Wire.available() == 1)
      data[i] = Wire.read();
  }
  // データを12ビットに変換します
  xAccl = ((data[1] * 256) + (data[0] & 0xF0)) / 16;
  if (xAccl > 2047)  xAccl -= 4096;
  yAccl = ((data[3] * 256) + (data[2] & 0xF0)) / 16;
  if (yAccl > 2047)  yAccl -= 4096;
  zAccl = ((data[5] * 256) + (data[4] & 0xF0)) / 16;
  if (zAccl > 2047)  zAccl -= 4096;
  xAccl = xAccl * 0.00098; // renge +-2g
  yAccl = yAccl * 0.00098; // renge +-2g
  zAccl = zAccl * 0.00098; // renge +-2g
  Serial.print("Accl= ");
  Serial.print(xAccl);
  Serial.print(",");
  Serial.print(yAccl);
  Serial.print(",");
  Serial.print(zAccl);
  Serial.println(""); 
  
  delay(1000);
}
以降では、スケッチ内で使用されているレジスタアドレスをピックアップして取り上げていきます。

BMX055レジスタへの書き込み

加速度センサーを初期化するためのレジスタアドレスと設定値です。
詳細はデータシートを参照。

ACC Register 0x0F (PMU_RANGE) Read/Write

このレジスタにより、加速度計の重力範囲を選択できます。 スケッチ内での設定値は0x03
Bit Resolution 7 6 5 4 3 2 1 0
Content reserved range
±2g range 0.98mg/LSB 0 0 0 0 0 0 1 1 0x03
±4g range 1.95mg/LSB 0 0 0 0 0 1 0 1 0x05
±8g range 3.91mg/LSB 0 0 0 0 1 0 0 0 0x08
±16g range 7.81mg/LSB 0 0 0 0 1 1 0 0 0x12
0: 予約値 / 赤字: 起動時の初期値

ACC Register 0x10 (PMU_BW) Read/Write

このレジスタにより、加速データフィルタの帯域幅を選択できます。 スケッチ内での設定値は0x08
Bit 7 6 5 4 3 2 1 0
Content reserved bw
7.81 Hz 0 0 0 0 0 x x x 0x00-0x07
0 0 0 0 1 0 0 0 0x08
15.63 Hz 0 0 0 0 1 0 0 1 0x09
31.25 Hz 0 0 0 0 1 0 1 0 0x0A
62.5 Hz 0 0 0 0 1 0 1 1 0x0B
125 Hz 0 0 0 0 1 1 0 0 0x0C
250 Hz 0 0 0 0 1 1 0 1 0x0D
500 Hz 0 0 0 0 1 1 1 0 0x0E
1000 Hz 0 0 0 0 1 1 1 1 0x0F
0 0 0 1 x x x x 0x10-0x1F
0: 予約値 / 赤字: 起動時の初期値

ACC Register 0x11 (PMU_LPW) Read/Write

メイン電力モードと低電力スリープ期間の選択。 スケッチ内での設定値は0x00。 主電源モードの構成設定:
Bit 7 6 5
Content suspend lowpower_en deep_suspend
NORMAL mode 0 0 0 0x00
DEEP_SUSPEND mode 0 0 1 0x20
LOW_POWER mode 0 1 0 0x40
未適応 0 1 1
SUSPEND mode 1 0 0 0x80
未適応 1 0 1
1 1 0
1 1 1
LOW_POWERモードでスリープフェーズ期間を設定:
Bit 4 3 2 1 0 NORMAL DEEP SUSPEND LOW POWER SUSPEND
Content sleep_dur reserved
0.5 ms 0 0 0 0 0 0x00 0x20 0x40 0x80
0 0 0 1 0 0x02 0x22 0x42 0x82
0 0 1 0 0 0x04 0x24 0x44 0x84
0 0 1 1 0 0x06 0x26 0x46 0x86
0 1 0 0 0 0x08 0x28 0x48 0x88
0 1 0 1 0 0x0A 0x2A 0x4A 0x8A
1 ms 0 1 1 0 0 0x0C 0x2C 0x4C 0x8C
2 ms 0 1 1 1 0 0x0E 0x2E 0x4E 0x8E
4 ms 1 0 0 0 0 0x10 0x30 0x50 0x90
6 ms 1 0 0 1 0 0x12 0x32 0x52 0x92
10 ms 1 0 1 0 0 0x14 0x34 0x54 0x94
25 ms 1 0 1 1 0 0x16 0x36 0x56 0x96
50 ms 1 1 0 0 0 0x18 0x38 0x58 0x98
100 ms 1 1 0 1 0 0x1A 0x3A 0x5A 0x9A
500 ms 1 1 1 0 0 0x1C 0x3C 0x5C 0x9C
1 s 1 1 1 1 0 0x1E 0x3E 0x5E 0x9E
0: 予約値 / 赤字: 起動時の初期値

BMX055レジスタへからの値の読み込み

加速度センサーから加速度を取得するためのレジスタアドレスを確認します。
詳細はデータシートを参照。

ACC Register 0x02 (ACCD_X_LSB) Read Only

レジスタにはXチャネル加速度読み出し値の最下位ビットが格納されます。 Xチャネル加速度値を読み取るとき、ACCD_X_MSBおよびshadow_dis='0'の前にACCD_X_LSBが読み取られるとACCD_X_MSBレジスタの値は読み取りが行われるまでロックされ、それによりデータの整合性が保証されています。バーストモード読み出しアクセスが実行される場合、この条件は本質的に満たされます。加速データは、電源投入時とDEEP_SUSPENDモード時を除き、ACCD_X_MSBからいつでも読み出すことができます。
Bit 7 6 5 4 3 2 1 0
Content acc_x_lsb undefined new_data_x
acc_x_lsb:最下位4ビットの加速リードバック値(2の補数形式)
undefined:使用されないランダムデータ
new_data_x:
  1. 加速度値が更新されていない場合
  2. 加速度値が更新されている場合

ACC Register 0x03 (ACCD_X_MSB) Read Only

このレジスタには、Xチャネル加速度読み出し値の最上位ビットが含まれています。 Xチャネル加速度値を読み取るとき、ACCD_X_MSBおよびshadow_dis=’0’の前にACCD_X_LSBが読み取られると、データの整合性が保証される。この場合、ACCD_X_LSBが読み取られた後、ACCD_X_MSBレジスタの値は、ACCD_X_MSBが読み取られるまでロックされます。バーストモードの読み取りアクセスが実行される場合、この条件は本質的に満たされます。加速データは、電源投入時およびDEEP_SUSPENDモード時を除き、ACCD_X_MSBからいつでも読み出すことができます。
Bit 7 6 5 4 3 2 1 0
Content acc_x_msb
acc_x_msb:加速リードバック値の最上位8ビット(2の補数形式)

ACC Register 0x04 (ACCD_Y_LSB) Read Only

このレジスタには、Yチャネル加速度読み出し値の最下位ビットが格納されます。 Yチャネル加速度値を読み出す場合、ACCD_Y_MSBおよびshadow_dis='0'の前にACCD_Y_LSBが読み取られるとACCD_Y_MSBレジスタの値は読み取りが行われるまでロックされ、それによりデータの整合性が保証されています。バーストモード読み出しアクセスが実行される場合、この条件は本質的に満たされます。加速データは、電源投入時およびDEEP_SUSPENDモード時を除き、ACCD_Y_LSBレジスタから読み出すことができます。
Bit 7 6 5 4 3 2 1 0
Content acc_y_lsb undefined new_data_y
acc_y_lsb:加速リードバック値の最下位4ビット(2の補数形式)
undefined:使用されないランダムデータ
new_data_y:
  1. 加速度値が更新されていない場合
  2. 加速度値が更新されている場合

ACC Register 0x05 (ACCD_Y_MSB) Read Only

このレジスタには、Yチャネル加速度読み出し値の最上位ビットが格納されます。 Yチャネル加速度値を読み出す場合、ACCD_Y_MSBおよびshadow_dis='0'の前にACCD_Y_LSBが読み取られるとACCD_Y_MSBレジスタの値は読み取りが行われるまでロックされ、それによりデータの整合性が保証されています。バーストモード読み出しアクセスが実行される場合、この条件は本質的に満たされます。加速データは、電源投入時およびDEEP_SUSPENDモード時を除き、ACCD_Y_MSBからいつでも読み出すことができます。
Bit 7 6 5 4 3 2 1 0
Content acc_y_msb
acc_y_msb:加速リードバック値の最上位8ビット(2の補数形式)

ACC Register 0x06 (ACCD_Z_LSB) Read Only

このレジスタには、Zチャネル加速度読み出し値の最下位ビットが格納されます。 Zチャネル加速度値を読み出す場合、ACCD_Z_MSBおよびshadow_dis='0'の前にACCD_Z_LSBが読み取られるとACCD_Z_MSBレジスタの値は読み取りが行われるまでロックされ、それによりデータの整合性が保証されています。バーストモード読み出しアクセスが実行される場合、この条件は本質的に満たされます。加速データは、電源投入時およびDEEP_SUSPENDモード時を除き、ACCD_Z_LSBレジスタから読み出すことができます。
Bit 7 6 5 4 3 2 1 0
Content acc_z_lsb undefined new_data_z
acc_z_lsb:加速リードバック値の最下位4ビット(2の補数形式)
undefined:使用されないランダムデータ
new_data_z:
  1. 加速度値が更新されていない場合
  2. 加速度値が更新されている場合

ACC Register 0x07 (ACCD_Z_MSB) Read Only

このレジスタには、Zチャネル加速度読み出し値の最上位ビットが格納されます。Zチャネル加速度値を読み出す場合、ACCD_Z_MSBおよびshadow_dis='0'の前にACCD_Z_LSBが読み取られるとACCD_Z_MSBレジスタの値は読み取りが行われるまでロックされ、それによりデータの整合性が保証されています。バーストモード読み出しアクセスが実行される場合、この条件は本質的に満たされます。加速データは、電源投入時およびDEEP_SUSPENDモード時を除き、ACCD_Z_MSBからいつでも読み出すことができます。
Bit 7 6 5 4 3 2 1 0
Content acc_z_msb
acc_z_msb:加速リードバック値の最上位8ビット(2の補数形式)

取得した値の処理

スケッチの中では以下のように処理されている。
ちなみに"?"の箇所には、"x"、"y"、"z"のいずれかが入る。
Accl = ((ACCD_MSB * 256) + (ACCD_LSB & 0xF0)) / 16;
まず、上位ビット"ACCD_MSB"の処理。
(ACCD_MSB * 256)
256 = 28の事なので、
ACCD_MSB <<8
とも表記できる。
やっている事は、
short int msb = 165;  // 二進数 0000000010100101
msb = msb <<8;        // 二進数 1010010100000000
8bit(1byte)の数値を、16bit(2byte)の上位ビットにシフトしているだけです。

次に下位8ビット"ACCD_LSB"の処理。
ACCD_LSB & 0xF0
AND演算子を使用して0xF0(二進数で'11110000')で比較。
AND演算は、比較する各ビットが1 : 1 だと1、それ以外では全て0となります。
よって、以下のような感じで演算。
short int lsb = 92;   // 二進数の 0000000001011100
lsb = lsb & 0xF0;     // 二進数の 0000000001010000
結果として下位4ビットを0としてクリアする事になります。

で、最終的には、2つの値を加算して16で除算。
((ACCD_MSB <<8) + (ACCD_LSB & 0xF0)) / 16
16 = 24の事なので、
((ACCD_MSB <<8) + (ACCD_LSB & 0xF0)) >>4
と表せる。4ビット右へシフトするので、12ビット(0〜4095)の値が得られる。

例えば、
accd = msb + lsb;   // 二進数の1010010101010000
accd >>4            // 二進数の0000101001010101
な感じ。

それに続けて行われている条件分岐では、
if (Accl > 2047)  Accl -= 4096;
取得した値は、2の補数なので2047(4096の半分)より大きければ、4096で減算する事で正負の符号を明確にしています。

最後に、ここまで得られた値にPMU_RANGEで設定したレンジに対応する分解能を乗算するわけですが。。。
Accl = Accl * 0.0098; // renge +-2g
サンプルのスケッチでは0.0098となっていますが、データシートでは、
±2g range 0.98mg/LSB 0x03
と、記載されているので、0.98mg/LSB = 0.00098g/LSBが正解な気がします。 なので、上のスケッチは0.00098に変更してあります。

"ここまで"のまとめ

スケッチの内容も理解出来たので、実際に動作させてみます。
--------------------------------------
Accl= -0.05,-0.10,1.00
--------------------------------------
Accl= -0.05,-0.10,1.00
--------------------------------------
Accl= -0.05,-0.10,1.00
--------------------------------------
Accl= -0.05,-0.10,1.00
--------------------------------------
Accl= -0.05,-0.10,1.00

Z軸に1Gがかかり、XY軸は微妙に傾斜している事を示しています。
このように元々のスケッチでも動作については全く問題ないのですが、ここまで知り得た情報を追記。
BMX055_ACCELEROMETER
#include <Wire.h>
// BMX055 加速度センサのI2Cアドレス  
#define Addr_Accl 0x18  // (JP1,JP2,JP3 = Closeの時)
// センサーの値を保存するグローバル関数
float Accl[3];

void setup()
{
  // Wire(Arduino-I2C)の初期化
  Wire.begin();
  // デバック用シリアル通信は9600bps
  Serial.begin(9600);
  //BMX055 初期化
  //------------------------------------------------------------//
  Wire.beginTransmission(Addr_Accl);
  Wire.write(0x0F);  // PMU_Rangeレジスタを選択
  Wire.write(0x03);  // Range = +/- 2g
  Wire.endTransmission();
  delay(100);
  //------------------------------------------------------------//
  Wire.beginTransmission(Addr_Accl);
  Wire.write(0x10);  // PMU_BWレジスタを選択
  Wire.write(0x08);  // Bandwidth = 7.81 Hz
  Wire.endTransmission();
  delay(100);
  //------------------------------------------------------------//
  Wire.beginTransmission(Addr_Accl);
  Wire.write(0x11);  // PMU_LPWレジスタを選択
  Wire.write(0x00);  // Normal mode, Sleep duration = 0.5ms
  Wire.endTransmission();
  delay(100);
}

void loop()
{
  //BMX055 加速度の読み取り
  short int data[6];
  Wire.beginTransmission(Addr_Accl);
  Wire.write(0x02);                     // データレジスタを選択
  Wire.endTransmission();
  Wire.requestFrom(Addr_Accl, 6); // 6バイトのデータを要求する
  for (int i = 0; i < 6; i++)
    data[i] = Wire.read();
  // データを変換する
  bitWrite(data[0], 15, bitRead(data[1], 7));
  Accl[0] = (((data[1] <<8) | data[0]) >>4) * 0.00098;
  bitWrite(data[2], 15, bitRead(data[3], 7));
  Accl[1] = (((data[3] <<8) | data[2]) >>4) * 0.00098;
  bitWrite(data[4], 15, bitRead(data[5], 7));
  Accl[2] = (((data[5] <<8) | data[4]) >>4) * 0.00098;
  Serial.print("Accl= ");
  Serial.print(Accl[0];
  Serial.print(",");
  Serial.print(Accl[1]);
  Serial.print(",");
  Serial.print(Accl[2]);
  Serial.println(""); 
  delay(300);
}
目新しく追記した箇所は、更新の値の取得とそれに基づく条件分岐、符号ビットの取得と格納、他は配列で管理するように変更したぐらいです。

で、これらで新規に使用したコマンドが以下。

bitRead()

説明

指定したビットの数値を読み取ります。

構文

bitRead(x,n)

パラメータ

x 読み取りたい数値変数の名前
n 読み取るビットの指定。最下位(右端)ビットの0から始まります。

戻り値

ビットの値(0または1)

bitWrite()

説明

数値変数にビットを書き込みます。

構文

bitWrite(x,n,b)

パラメータ

x 書き込む対象となる数値変数
n どのビットに書き込むか,最下位ビット(LSB)側が0。
b 書き込むビットの値(0または1)。
スケッチの内容は理解出来ましたが、現時点では加速度センサーを使っていると言うよりは、傾斜センサーを使っている印象です。

実用的にするためには、ここから重力方向を基準に加速度ベクトルを求め、加速度による移動距離推定などを計算したり、ジャイロセンサと組み合わせて回転角の精度を高めるなどする必要があります。この辺りについては改めて記載出来ればと思っています。

おまけ

加速度センサのレジスタは、他にも様々な機能に割り当てられています。
残念ながら、ここで全てを解説できる知識も技術も持ち合わせていないので、実際に触ってみて理解出来た"一部"だけを紹介しておきます。

ACC Register 0x08 (ACCD_TEMP) Read Only

レジスタには、2の補数形式で表される現在のチップ温度が含まれています。 temp <7:0>の読み出し値は、0x00 = 23°Cと換算して計算します。 また、温度の変化は0.5K(kelvin)/LSB単位で変化します。
Bit 7 6 5 4 3 2 1 0
Content temp
temp:温度値(2の補数形式)
動作するスケッチは下記。
BMX055_ACC_TEMP
#include <Wire.h>
// BMX055 加速度センサのI2Cアドレス  
#define Addr_Accl 0x18

void setup()
{
  // Wire(Arduino-I2C)の初期化
  Wire.begin();
  // デバック用シリアル通信は9600bps
  Serial.begin(9600);
}

void loop()
{
  float temp;
  Wire.beginTransmission(Addr_Accl);
  Wire.write(0x08);// データレジスタを選択
  Wire.endTransmission();
  Wire.requestFrom(Addr_Accl, 1);// 1バイトのデータを要求する
  temp = Wire.read();
  if (temp > 0x7F) temp -= 256;
  temp = (temp*0.5)+23;
  Serial.println(temp); 
  delay(300);
}
以降で加速度と少々異なる箇所だけをピックアップ。
if (temp > 0x7F) temp -= 256;
2の補数形式に対応して、1byteの数値を127を基準に正負に二分しています。
temp = (temp*0.5)+23;
これは、1bitに対して0.5K変化するので、得られた値に対して0.5を積算。
また、本来ケルビンから摂氏への変換は、
℃ = K - 273.15
となるのですが、K=0x00、つまりK=0の場合に23℃となる事が前提なので、23を加算しています。

実行結果。
26.00
26.00
26.00
25.50
25.50
25.50

まぁ、これを使用するほど厳密な精度を求める事もないと思うのですけどね。

0 件のコメント:

コメントを投稿