AK8963(mpu9250内蔵)地磁気センサーの利用めも

mpu9250に内蔵してある地磁気センサーを利用すれば、磁石の移動を検知できるのではと思い、ちょっと調べてみたのでまとめます。

https://strawberry-linux.com/pub/AK8963.pdf (データシート、日本語なのでわかりやすい)
https://asukiaaa.blogspot.jp/2017/07/arduinompu9250.html?showComment=1523319516277#c5671704556111194904 (まず、動かすのにはこのサイトがわかりやすい)
https://cdn.sparkfun.com/assets/learn_tutorials/5/5/0/MPU-9250-Register-Map.pdf(mpu9250のRegisterMap)

AK8963 地磁気センサー
スレーブアドレス : 0x0C (配線で変更できそうでありますが,直接読み出すとこの値が出てきます。0x68はmpu9250自体のアドレス。)
mpu9250のデータシートより
地磁気センサーは3rdPartyの補助的な(auxiliary)センサーであります。
BypassMuxに設定が必要かはわかりませんが、初期状態で通信できたので必要なさそうです。
(追記)
先に挙げたサイトのおかげで設定が必要ない状態になっていただけで、mpu9250側の設定は必要です。設定は0x37に2を書き込むことでBypassモードにしてくれます(mpu9250ーregisterデータシート p.29)

デフォルトのレジスタ値は次のようになります。(追記:デフォルトではなかったです)
0->48  //who
1->9A  //ID
2->3    //データレディ、読み飛ばし
3->B6 //センサデータ
4->FF
5->9F
6->0
7->83
8->0 //ここまでセンサデータ
9->0   //HOFL(オーバーフローすると1),BITM(出力ビット幅0->14bit ,1->16bit)
A->2  //MODE,BITM(出力ビット幅0->14bit ,1->16bit)
B->0
C->0
D->FF
E->FF
F->0
10->B5 //感度ROM(書き換え不可)下二つも
11->B6
12->AA
13->0


読み出しの注意点はST2を必ず最後に読む事みたいです。そうしないと、読み出し中だとセンサーが判断するみたいです。
モードは単発測定、連続測定(max100hz),etcありますが、連続以外は使わないので調べません。

MPU9250の0x37->0x02でBypassMode
モード選択(CTRL2のSRSTはSoftResetなのでいじらない):0x0A->0x06で連続測定
データレディーが出るまで待つ(ST1読み込み)0x0A監視
 データ読み込み
 ST2読み込み (アドレスが連続しているので連続読み可能)
待つに戻る

をプログラムにすると
#include <Wire.h>

int sai=0;
byte ST2;
byte xh=0;
byte xl=0;
byte yh;
byte yl;
byte zh;
byte zl;
int count;
long sum;

void setup()
{
  Wire.begin();

  Serial.begin(9600);
  Serial.println("Testing");
  writeRegM(0x37,0x02);
  writeReg( 0x0A ,0x16);                 //100Hz連続モード
  count=0;
  sum=0;
}
void loop() {
  byte ST1=readReg(0x02);
  if((ST1&0x01)==1){ //Date Ready
    burstReadReg();
    int x=( (xh<<8)| xl);
    sum +=x; 
    count++;
  }
  if(count>=128){
    Serial.print(ST1);
    Serial.print("  ");
    Serial.println(sum>>7);
    sum=0;
    count=0;
  }
}
byte readReg(byte address){
  byte anser;
  Wire.beginTransmission(0x0C);
  Wire.write(address);
  Wire.endTransmission();
  Wire.requestFrom(0x0C,1);
  while(Wire.available()){
    anser=Wire.read();
  }
  return anser;
}
void burstReadReg(){
  byte anser;
  Wire.beginTransmission(0x0C);
  Wire.write(0x03);
  Wire.endTransmission();
  Wire.requestFrom(0x0C,7);
  xl=Wire.read();
  xh=Wire.read();
  yl=Wire.read();
  yh=Wire.read();
  zl=Wire.read();
  zh=Wire.read();
  ST2=Wire.read();
}

void writeReg(byte address, byte data) {
  Wire.beginTransmission(0x0C);
  Wire.write(address);
  Wire.write(data);
  Wire.endTransmission();
  Wire.endTransmission();
}
void writeRegM(byte address, byte data) {
  Wire.beginTransmission(0x68);
  Wire.write(address);
  Wire.write(data);
  Wire.endTransmission();
  Wire.endTransmission();
}
こんな感じです。HTML的にコードが変わってしまって、難しかったので直に張ります。
プラマイ3程度数値が振動します。これは地磁気の振動ではなく、おそらくホールセンサは電圧を測ることになるので電波などによる磁界の揺れをとらえているのではないかと私は考えています。とりあえずノイズですね。
128回集計し平均化すると、全く揺れなくなりました。
byte ST1=readReg(0x02);
  if((ST1&0x01)==1){ //Date Ready
    burstReadReg();
    int x=( (xh<<8)| xl);
    sum +=x;    
    count++;
  }
  if(count>=128){
    Serial.print(ST1);
    Serial.print("  ");
    Serial.println(sum>>7);
    sum=0;
    count=0;
  }
こんな感じにプログラム変更

とりあえずいい感じなので、次に、感度調整、オフセット調整を考えていこうと思います。
追記
地磁気レベルの磁力ではなかなかむずかしいです。
どんな風に難しいかというと
1軸に固定した状態(水平にした状態で1点で固定した状態)で回転させ、各軸のとる値の最大最小をとったところ、毎回10ほど(16bit)違う値になります。
x->116~-86 
y->442~258
z->148~11
x、yが水平でなくても
最大と最小の大きさは一致するはずであるのでちょっと問題がありそうです。
->そんなことない。間違い。
仮に、地磁気ベクトルが水平面とBだけの角度を持ち大きさRとし、AK8963のx軸が水平面とAだけの角度を持っているとすると
最小はRcos(180-A-B) = -R{cosB cosA -sinB sinA}
最大はRcos(B-A) = R{ cosB cosA + sinB sinA}
つまり、RsinBsinAが変位の中心となります。



電圧を変えてみたところ
4.38V->5.05V 変化はありませんでした。いいですね。
<8 address="" anser="" burstreadreg="" byte="" data="" ire.available="" pre="" readreg="" return="" serial.print="" serial.println="" st2="Wire.read();" void="" while="" wire.begintransmission="" wire.endtransmission="" wire.requestfrom="" wire.write="" writereg="" writeregm="" x03="" x0c="" x68="" x="" xh="Wire.read();" xl="Wire.read();" yh="Wire.read();" yl="Wire.read();" zh="Wire.read();" zl="Wire.read();">



コメント