I2C接続の加速度センサー(MMA8451)を使ってみる

デバイスアドレス確認( i2c_scanner)
⑤ I2C 通信 (1)で、温度センサー(ADT7410 )と加速度センサー(MMA8451)を同じ I2C シリアルバス上に接続しました。ということで、今回は、加速度センサー(MMA8451)の確認をしてみます。


加速度センサーと温度センサー  I2C 接続

先ずはデバイスアドレス確認ですが、このセンサーは先回の温度センサー(ADT7410 )と異なり、デバイスアドレスを設定する外部端子がありません。調べると  ‘ i2c_scanner’  なるプログラムがarduinoのサイトにありましたのでこれを使ってみます。プログラム内容は、1~127までのアドレスを順番に実際に接続し、接続できるものを出力するといったものの様です。従って、どのアドレスが使用されているかは判りますが、たくさんのデバイスが接続されている場合、どのデバイスがどのアドレスかまでは判りませんので、確認時は注意が必要です。

I2C デバイスススキャンプログラム(接続アドレス確認用

#include <Wire.h>

void setup()
{
  Wire.begin();

  Serial.begin(9600);
  while (!Serial);             // Leonardo: wait for serial monitor
  Serial.println("\nI2C Scanner");
}


void loop()
{
  byte error, address;
  int nDevices;

  Serial.println("Scanning...");

  nDevices = 0;
  for(address = 1; address < 127; address++ ) 
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");

      nDevices++;
    }
    else if (error==4) 
    {
      Serial.print("Unknown error at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");

  delay(5000);           // wait 5 seconds for next scan
}

〈 引用元:https://playground.arduino.cc/Main/I2cScanner 〉

シリアルモニタを起動し、プログラム実行結果を確認します。下図の様に、‘0x1D’ , ‘0x48’  二つのデバイスアドレスを検出します。‘0x48’は温度センサーのデバイスアドレスなので、加速度センサーのデバイスアドレスは、‘0x1D’ と判断します。

 

加速度センサーデータ取得プログラム
加速度センサーデータ取得プログラム例を以下に示します。

加速度センサー(MMA8451)用プログラム

#include <Wire.h>

#define DVC_ADRS 0x1D                           // デバイスアドレス
#define GRAV_E       (9.80665F)                 // 標準重力加速度

uint16_t x_r , y_r , z_r ;                      // XYZ 14bitデータ
long int x_v , y_v , z_v ;                      // XYZ 数字
float x_g , y_g , z_g ;                         // XYZ 角加速度
float z_sts ;

// ①通信開始、デバイス初期化
bool DVC_INIT(uint8_t i2caddr){
    Wire.begin();                               // 接続開始
  
    wrt_reg(0x2B, 0x40);                        // DEVICE RESET ENABLED
    while (read_reg(0x2B) & 0x40);              // デバイスリセット待ち
    wrt_reg(0x0E , 0x01);                       // RANGE 設定
  
    wrt_reg(0x2B, 0x02);                        // PowerMode( 0x00:normal,0x01:Low Noise Low Power,0x02:High Resolution, 0x03:Low Power)
    wrt_reg(0x2D, 0x01);                        // INT_EN_DRDY
    wrt_reg(0x2E, 0x01);                        // INT_CFG_DRDY

    wrt_reg(0x11, 0x40);                        // PL_EN
    wrt_reg(0x2A, 0x01 | 0x04);                 // ACTIVE , LNOISE
    return true;   
}

// ②指定レジスタにデータ書き込み
void wrt_reg(uint8_t reg, uint8_t value) {
    Wire.beginTransmission(DVC_ADRS);           // デバイス指定、通信開始
    Wire.write((uint8_t)reg);                   // レジスタ指定
    Wire.write((uint8_t)(value));               // データ書込
    Wire.endTransmission();                     // 送信完了
}

// ③指定レジスタからデータを読み出し
uint8_t read_reg(uint8_t reg) {
    Wire.beginTransmission(DVC_ADRS);           // 送信処理開始
    Wire.write(reg);                            // レジスタ指定
    Wire.endTransmission(false);                // 送信完了(コネクション維持)
    Wire.requestFrom(DVC_ADRS, 1);              // 1byteデータに要求

    if (! Wire.available()) return -1;          // データ有無判定
    return (Wire.read());                       // 1byteデータ  
}

// ④データ取得、変換
void read_data(void) {
    Wire.beginTransmission(DVC_ADRS);           // 送信処理開始
    Wire.write(0x01);                           // アドレス指定
    Wire.endTransmission(false);                // 送信完了(コネクション維持)
    Wire.requestFrom(DVC_ADRS, 6);              // 6byteデータ要求
                                                //
    // データ取得、変換
    x_r = Wire.read() ; x_r <<= 8 ; x_r |= Wire.read() ; x_r >>= 2;
    y_r = Wire.read() ; y_r <<= 8 ; y_r |= Wire.read() ; y_r >>= 2;
    z_r = Wire.read() ; z_r <<= 8 ; z_r |= Wire.read() ; z_r >>= 2;

    x_v = (long int)x_r ; y_v = (long int)y_r ; z_v = (long int)z_r ;    

    // マイナス値変換
    if(x_r & 0x2000) x_v = x_v - 16384 ;        
    if(y_r & 0x2000) y_v = y_v - 16384 ;
    if(z_r & 0x2000) z_v = z_v - 16384 ;
 
    // スケールレンジを取得
    uint8_t range = read_reg(0x0E) & 0x03;
    uint16_t div_p = 1;

    // 加速度変換係数(測定レンジにより異なる)
    if (range == 0x02) div_p = 1024 ; 
    if (range == 0x01) div_p = 2048 ;
    if (range == 0x00) div_p = 4096 ;
    x_g = (float)x_v * GRAV_E / div_p ; 
    y_g = (float)y_v * GRAV_E / div_p ; 
    z_g = (float)z_v * GRAV_E / div_p ;

    //姿勢判定(Z方向)
    //参考:http://www.robotsfx.com/robot/TriAxisSen.html
    z_sts = 0.7071 * GRAV_E ;
    if(z_g > z_sts){
        Serial.print("▲▲▲▲▲ ( ");
    }else if(z_g  > -1 * z_sts){
        Serial.print("◇◇◇◇◇ ( ");
    }else{
        Serial.print("▼▼▼▼▼ ( ");
    }
    Serial.print(z_g) ; Serial.print(" (判定値:± ") ;  Serial.print(z_sts) ; Serial.println("))") ;
}

// ⑤最初に1回だけ行う処理
void setup() {
    Serial.begin(9600);
    // デバイスアドレス指定し、初期設定
    if(!DVC_INIT(DVC_ADRS))  {
        Serial.println("-----");
        while (1);
    }
                                                  
    uint8_t reg1 = read_reg(0x2A);              // 元設定確認
    wrt_reg(0x2A, 0x00);                        // StandByMode
    wrt_reg(0x0E, 0x00 & 0x3);                  // Set Range( 0x00:2g , 0x01:4g , 0x02:8g , 0x03:reserved )
    wrt_reg(0x2A, reg1 | 0x01);                 // ActiveMode
}

// ⑥繰り返し処理(センサーを読んで、シリアル通信出力)
void loop() {
    read_data();                                // XYZ軸データ取得

    // XYZ取得値出力 ・ 角加速度データ出力(m/s^2)
    Serial.print("X:\t"); Serial.print(x_v) ; Serial.print("\t"); Serial.print(",   X m/s^2:\t"); Serial.print(x_g); Serial.print("\n");
    Serial.print("Y:\t"); Serial.print(y_v) ; Serial.print("\t"); Serial.print(",   Y m/s^2:\t"); Serial.print(y_g); Serial.print("\n");
    Serial.print("Z:\t"); Serial.print(z_v) ; Serial.print("\t"); Serial.print(",   Z m/s^2:\t"); Serial.print(z_g); Serial.print("\n");
    Serial.println() ; Serial.println() ;
    delay(2500);
}

プログラムのおおよそのフローは、大体下記表の通りです。⑤⑥から、①~④の関数を呼び出す構成としています。

① DVC_INIT デバイス初期設定。
② wrt_reg レジスタアドレス、設定値を受け取り、書き込みを実行します。
③ read_reg 指定レジスタアドレス読込実行。
④ read_data センサー値取得。データレジスタアドレスは次の通りです。(X・Y・Z軸、各2byte構成)
0x01(X軸上位) , 0x02(X軸下位)、0x03(Y軸上位) , 0x04(Y軸下位)、0x05(Z軸上位) , 0x06(Z軸下位)
基本的にはADT7410温度センサーデータ処理と同様。温度センサーでは13bitデータを扱いましたが、本センサーは14bitデータとなり、bitのシフト数等が処理が変わっています。加速度計算は取得値に標準加速度と係数を掛けて求めています。係数測定レンジによって変わるようです。十分理解出来ませんでしたが、今回設定レンジにて結果出力がよさそうなので、良しとして次に進むこととしました。ご了承下さい。
⑤ setup 起動時1回だけ実行。① DVC_INITを呼び出し、デバイスの初期設定。測定レンジ設定。
⑥ loop 繰り返し処理。④ read_data を呼び出し、データ取得・加速度変換、表示用出力実行。

 


傾き角度計算

個人的には加速度センサーの必要性をあまり感じていないのですが、スマホやおもちゃなどの姿勢(傾き)検出に使える様です。今回のプログラムでも大雑把な姿勢検出を行ってみました。いろいろ参考サイトを確認しましたが、浅草ギ研さんのサイトが簡単で素人の私には判りやすいと思い利用させて頂いています。

重力加速度(G)= Sin(傾き角度)
  (引用 : http://www.robotsfx.com/robot/TriAxisSen.html)

アークサイン計算で、傾きが求められるとのことですが、マイコンの処理は複雑でメモリ不足等になることもある様ですので、予め計算した値に基づき簡単な姿勢判定を行います。

Z軸方向加速度は、平面設置時1G(0度),直角設置時0G(90度),反転設置時ー1G (180度) の値を示します。同様に45度:0.7G、135度:-0.7Gとなります。
( 1G=9.80665 m/s2
プログラム(行番:78~87)内では、0.7Gを超える加速度を検出した場合、”▲▲▲▲▲”、ー0.7G以下の加速度を検出した場合、”▼▼▼▼▼”、その他(-0.7G<(検出値)<=0.7G)の場合、“◇◇◇◇◇”を出力しています。

結果の確認
プログラムの実行結果を確認してみます。下の写真の様に加速度センサーの傾きを変えて、シリアルモニターへのデータ出力結果を確認します。

下のシリアルモニタ出力結果の通り、Z方向加速度の値によって、角姿勢に対応する予め設定した記号(▲・◇・▼)が表示されることが確認出来ました。

以上、加速度センサー(MMA8451)接続についてです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です