STM32F3 Discovery、電子水平器の作成

STM32F3 Discovery、電子水平器の作成


前回、STM32F3 Discovery のLSM303DLHC磁気センサーを使って電子コンパスを作成しましたが、このICにはもう一つ、加速度センサーが搭載されています。
今回は、この加速度センサーを使って水平器を作成してみたいと思います。


そして、今回も外部回路は無しで、表示は8つのLEDだけで実現してみたいと思います。


LSM303DLHCの加速度センサーの使い方


LSM303DLHCの加速度センサーは、XYZの3軸に対する加速度を検出します。


各軸の加速度は、各軸の加速度を検出しています。
ボードの表側を上にして水平な状態で静止できたとするならば、Z軸が鉛直方向となり1Gが検出され、XY軸は0Gとなります。
傾いた状態、鉛直方向からの角度θで得られるCos()を1Gに掛けたものとなります。
つまり、軸が鉛直方向の場合θは0度となりCos(0)=1で9.8G、軸が45度の場合はCos(45)=0.707...となり6.9、90度の場合はCos(90)=0となり0、逆さまにするとCos(180)=-1で-9.8となります。


検出されるデーターについて


得られるデーターが割と曲者です。

データーシートには書いてありませんが、データーを取得すると下位4ビットは必ず0なので、実は得られるデーターは12bit、下4ビットを無視する必要があります。

データーシートによると、得られる数値はFSビットによって設定されるとあります。
CTRL_REG4_A(23h)のFS[0:1]でスケールを設定します。デフォルトは00で ±2G のレンジとなり、取得できるデータは、1 mg/LSB となります。

この、1 mg/LSB はLSB1ビットあたりの単位を示しています。
仮にある軸のデータが 0x001 となった場合の加速度を求めると、
= 1mg = 0.001G = 0.00980665m/s2 となり、
1000の値を取得できたときは、
1000 * 0.00980665m/s2 = 9.80665m/s2 と、なります。

スケールを ±4G とした場合は、2mg/LSBとなるので、
0x001 = 0.002G = 0.0196013(0.00980665 x 2)m/s2 です。

逆さまにした時は、12ビットを符号拡張して得られる負の数値となります。

ですが、微妙に±1gとならないのは0gレベルのオフセットを
ちゃんと設定する、或いはキャリブレーションなどしないとならないようです。
(このページのコードはキャリブレーションしていませんのでご了承ください)


今回の実装仕様


今回は、XYZ3つの軸を使います。

使い方はボードを水平に持ち、その時の傾きでLEDを変化させます。

ある方向に、45度以上傾いていた場合はLEDは全て消灯
45度以下その方向のLEDだけを点灯させます。
30度以下では隣接する3つのLEDを点灯し、
10度以下では半分のLEDを点灯、
全体が5度以下の傾きとなったとき、全てのLEDを点灯します。


計算方法


誤差があるようなので、キャリブレーションが必要と書きましたがやっていません。
まず、Z軸の傾きを確認します。

Z軸で得られた値にmGを掛けると加速度が求まります。
その加速度は、COS(傾き)値が掛けられた数値なので、Z軸の加速度を重力加速で割った値を求め、その値から ACOS() で傾きを求められます。

今回利用するスケールは ±2 なので、1ビットあたり mG ですので、mG を掛けて G で割るので G は消去できるため、Z軸の値を単純に1/1000した値をACOS()すればよいことになります。

次にXY軸の合成ベクトルを算出し、傾いている方向を出します。(計算方法は前回の方角と同じ)
傾き加減で、傾いている方向の周辺のLEDをいくつ点灯するか決定します。


I2C通信方法


加速度センサーは書き込みが0x32、読み込みが0x33です。

加速度センサーを設定します。レジスタ、0x20に0x27、0x23に0x00を書き込みます。
計測値は、0x28~0x2Dの連続したレジスタがそれぞれ、XL、XH、YL、YH、ZL、ZHになります。

XYZの並びや、エンディアンが前回の地磁気センサーとは異なっています。
読み込みも地磁気センサーとは異なり連続で読み出すことはできないようです。1バイトづつ読み出してあげる必要があります。

地磁気センサーと加速度センサーの設計を別のチームで行って1つのチップにでもしたんでしょうかね?


1.プロジェクトの生成


STM32 CubeMX を使ってプロジェクトを生成します。
デフォルトの STM32F3 Discovery 設定としてもよいですが、今回必要なPINは下記のとおりです。(前回と同じ)

 ・I2C(Connectivity I2C1 enabled)
   PB7 --> I2C1 SDA
   PB6 --> I2C1 SCL

 ・LED(GPIO Output)
   PB8 --> LD4 Blue LED
   PB9 --> LD3 Red LED
   PB10 --> LD5 Orange LED
   PB11 --> LD7 Green LED
   PB12 --> LD9 Blue LED
   PB13 --> LD10 Red LED
   PB14 --> LD8 Orange LED
   PB15 --> LD6 Green LED

プロジェクトタブでプロジェクト名をつけて、
ココでは、下記のように設定しました。

 Project Name = I2CDevice
 Toolchain/IDEにSW4STM32を選択、Generate Under Rootのチェックを外す

"GENERATE CODE"を実行してプロジェクトを生成してください。

C/C++パースペクティブなどでプロジェクトエクスプローラーに生成したプロジェクトができていることを確認してください。


コード記述


プロジェクトエクスプローラーから、作成したプロジェクトを開きます。
Application --> User --> main.c を選択、ダブルクリックで開きます。
このソースに対して、コードを追加します。

Define定義

#define COS_1 0.38268343f
#define COS_2 0.92387953f

#define DEG_05  0.08726646f  // π/36 = 5.0
#define DEG_10  0.17453292f  // π/18 = 10.0
#define DEG_30  0.52359877f  // π/6  = 30.0
#define DEG_45  0.78539816f  // π/4  = 45.0

I2Cコマンド定義

  uint8_t acc_init1[] = {0x20, 0x27};
  uint8_t acc_init2[] = {0x23, 0x00};
  uint8_t acc_data[] = {0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D};

LSM303DLHC加速度センサー設定 メインループの前で、センサーを設定します。

  HAL_I2C_Master_Transmit(&hi2c1, 0x32, acc_init1, 2, 1000);
  HAL_I2C_Master_Transmit(&hi2c1, 0x32, acc_init2, 2, 1000);

メインループ
なんだかダラダラ書いていますが、殆どがLED制御のコードです。
力技で書いてしまいましたがご容赦を。

  uint8_t buff[6];
  short X, Y, Z;
  int ix, iy;
  float xf, yf, zf, l, q;

  while (1)
  {

   X = Y = Z = 0;

   memset(buff, 0x00 , sizeof(buff));
   HAL_I2C_Master_Transmit(&hi2c1, 0x32, &acc_data[0], 1, 1000);
   HAL_I2C_Master_Receive(&hi2c1, 0x33, &buff[0], 1, 1000);
   HAL_I2C_Master_Transmit(&hi2c1, 0x32, &acc_data[1], 1, 1000);
   HAL_I2C_Master_Receive(&hi2c1, 0x33, &buff[1], 1, 1000);
   HAL_I2C_Master_Transmit(&hi2c1, 0x32, &acc_data[2], 1, 1000);
   HAL_I2C_Master_Receive(&hi2c1, 0x33, &buff[2], 1, 1000);
   HAL_I2C_Master_Transmit(&hi2c1, 0x32, &acc_data[3], 1, 1000);
   HAL_I2C_Master_Receive(&hi2c1, 0x33, &buff[3], 1, 1000);
   HAL_I2C_Master_Transmit(&hi2c1, 0x32, &acc_data[4], 1, 1000);
   HAL_I2C_Master_Receive(&hi2c1, 0x33, &buff[4], 1, 1000);
   HAL_I2C_Master_Transmit(&hi2c1, 0x32, &acc_data[5], 1, 1000);
   HAL_I2C_Master_Receive(&hi2c1, 0x33, &buff[5], 1, 1000);
   HAL_I2C_Master_Transmit(&hi2c1, 0x32, &acc_data[6], 1, 1000);
   HAL_I2C_Master_Receive(&hi2c1, 0x33, &buff[6], 1, 1000);

   X = ((short)buff[1] << 8) | (short)buff[0];
   X = X >> 4;
   xf = X;

   Y = ((short)buff[3] << 8) | (short)buff[2];
   Y = Y >> 4;
   yf = Y;

   Z = ((short)buff[5] << 8) | (short)buff[4];
   Z = Z >> 4;
   zf = Z;

   zf = zf / 1000;
   zf = acosf(zf);

   if(X < 0) ix = 1;
   else ix = 0;
   xf = fabsf((float)X);

   if(Y < 0) iy = 1;
   else iy = 0;
   yf = fabsf((float)Y);

   l = sqrtf(powf(xf, 2.0) + powf(yf, 2.0));
   q = xf / l;

   if(zf < DEG_05) {  // 5.0DEG
   HAL_GPIO_WritePin(GPIOE,
    GPIO_PIN_8  | GPIO_PIN_9  | GPIO_PIN_10 | GPIO_PIN_11 |
    GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15,
    GPIO_PIN_SET);
  }
  else if(zf < DEG_10) {  // 10.0DEG
    if(ix) {
     if(iy) {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_12 | GPIO_PIN_13, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_14 | GPIO_PIN_15 | GPIO_PIN_8, GPIO_PIN_RESET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_13 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_14 | GPIO_PIN_15, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10, GPIO_PIN_RESET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_12 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_15 | GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_RESET);
      }
     }
     else {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_15 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12, GPIO_PIN_RESET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_13 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_14 | GPIO_PIN_15, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10, GPIO_PIN_RESET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_14 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_15 | GPIO_PIN_8, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11, GPIO_PIN_RESET);
      }
     }
    }
    else {
     if(iy) {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_12 | GPIO_PIN_13, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_14 | GPIO_PIN_15 | GPIO_PIN_8, GPIO_PIN_RESET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9 | GPIO_PIN_15 | GPIO_PIN_8 | GPIO_PIN_10 | GPIO_PIN_11, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14, GPIO_PIN_RESET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_10 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_11 | GPIO_PIN_12, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15, GPIO_PIN_RESET);
      }
     }
     else {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_15 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12, GPIO_PIN_RESET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9 | GPIO_PIN_15 | GPIO_PIN_8 | GPIO_PIN_10 | GPIO_PIN_11, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14, GPIO_PIN_RESET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8 | GPIO_PIN_14 | GPIO_PIN_15 | GPIO_PIN_9 | GPIO_PIN_10, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13, GPIO_PIN_RESET);
      }
     }
    }
  }
  else if(zf < DEG_30) {  // 30.0DEG
    if(ix) {
     if(iy) {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11 | GPIO_PIN_10 | GPIO_PIN_12, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15 | GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_RESET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_13 | GPIO_PIN_12 | GPIO_PIN_14, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_15 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11, GPIO_PIN_RESET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_12 | GPIO_PIN_11 | GPIO_PIN_13, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_14 | GPIO_PIN_15 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10, GPIO_PIN_RESET);
      }
     }
     else {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_15 | GPIO_PIN_14 | GPIO_PIN_8, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13, GPIO_PIN_RESET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_13 | GPIO_PIN_12 | GPIO_PIN_14, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_15 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11, GPIO_PIN_RESET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_14 | GPIO_PIN_13 | GPIO_PIN_15, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12, GPIO_PIN_RESET);
      }
     }
    }
    else {
     if(iy) {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11 | GPIO_PIN_10 | GPIO_PIN_12, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15 | GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_RESET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_10, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15, GPIO_PIN_RESET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_10 | GPIO_PIN_9 | GPIO_PIN_11, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15 | GPIO_PIN_8, GPIO_PIN_RESET);
      }
     }
     else {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_15 | GPIO_PIN_14 | GPIO_PIN_8, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13, GPIO_PIN_RESET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_10, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15, GPIO_PIN_RESET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8 | GPIO_PIN_15 | GPIO_PIN_9, GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14, GPIO_PIN_RESET);
      }
     }
    }
  }
  else if(zf < DEG_45) {  // 45.0DEG
   HAL_GPIO_WritePin(GPIOE,
    GPIO_PIN_8  | GPIO_PIN_9  | GPIO_PIN_10 | GPIO_PIN_11 |
    GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15,
    GPIO_PIN_RESET);
    if(ix) {
     if(iy) {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11, GPIO_PIN_SET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_13, GPIO_PIN_SET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_12, GPIO_PIN_SET);
      }
     }
     else {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_15, GPIO_PIN_SET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_13, GPIO_PIN_SET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_14, GPIO_PIN_SET);
      }
     }
    }
    else {
     if(iy) {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11, GPIO_PIN_SET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, GPIO_PIN_SET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_10, GPIO_PIN_SET);
      }
     }
     else {
      if(q < COS_1) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_15, GPIO_PIN_SET);
      }
      else if (q > COS_2) {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, GPIO_PIN_SET);
      }
      else {
       HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8, GPIO_PIN_SET);
      }
     }
    }
  }
  else {
   HAL_GPIO_WritePin(GPIOE,
    GPIO_PIN_8  | GPIO_PIN_9  | GPIO_PIN_10 | GPIO_PIN_11 |
    GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15,
    GPIO_PIN_RESET);
  }
  }



使い方


STM32F3 Discoveryボードを水平に持ちます。
水平になっていないと、下の方のLEDが点灯します。
角度が大きすぎる場合は何も点灯しません。 うまく傾きを調整し、5度以下にできるとLEDが全点灯します。

何に使えるか?


今回は水平器として利用しましたが、ソレ以外のいみちとして、
・乗り物や建物物体の加速状況、揺れや実際にかかるGを計測する。
  遊園地の絶叫系解析に
   高層の最上階は本当に揺れているのだろうか?
 ・ロボット本体の姿勢制御、ローボットアームの力制御。
 ・傾いたらビリビリするお仕置きマシーン

など、どちらの方向にどのようなGが加わっているか検出したい時に利用できます。


最後に


データーシートが難解?なので使い方がよくわからん、という人も多いみたいですね。
このセンサー。

キャリブレーションしないと精度が上がらない感じですが、個体差がどのくらいあるのか気になります。 もし、なにかに使う場合には、軸ごとの誤差の相関関係なども見ないといけませんが今回の記事はセンサーの評価ではありませんので。


Lightning Brains

コメント

このブログの人気の投稿

Linuxシステムコール、共有メモリの使い方

Linuxシステムコール、メッセージキューの使い方

Linuxシステムコール、セマフォの使い方