SSブログ
English Version

I2C通信実験 3軸磁気センサー [PIC]

 Amazon で購入した3軸デジタルコンパス GY-271(HMC5883L使用)を使って PIC24FJ64GA002上で動作するpicle言語で I2C 通信実験をやってみました。
 I2Cの1chはシリアル通信に使っているピンとぶつかるので(config2のI2C1SEL設定でpinアサイン変更可ですが)I2Cの2ch を使って実験しました。
 PIC24FJ64GA002への接続は
  • 6ピン:SDA2(データ)
  • 7ピン:SCL2(クロック)

となります。(それぞれ2.2K程度の抵抗でプルアップする
★追記 2016/05/05 GY-271のボード内で 4.7K でプルアップ済みなので外付けのプルアップ抵抗は不要

 I2C通信は基本的なデータの送受信方法は決まっていますが、接続相手依存でシーケンスが変わるので試行錯誤的にプログラム変更して作ることになりがちです。このような場合、コンパイル&フラッシュ書込みがメッチャ速い picle コンパイラ が便利です^^
 また、コンパイラ化したことでインタープリタでは対応できなかった今回のようなケースでも使えます。

 今回作成したプログラムリストを末尾に付けておきます。(I2C関連関数でタイムオーバーチェックしていますが無くても大丈夫だと思います
 ★追記 2016/05/05 タイムオーバーチェックを削除し、ステータスフラグチェックを強化
 データ受信領域はワード配列(RdData)とバイト配列(_RdData)を重ねて(C言語のunionもどき)、I2Cからの受信バイト列を格納する時に上位バイトと下位バイトを入れ替えています(少しトリッキーかもしれないけど・・)。また最終データ受信時にはNAK応答するのが正規ですが処理簡略化のためACK応答のままにし、最後にストップコンディションを行っています。
 ★追記 2016/05/06 磁石を近づけるとたまに固まる(SDAがlowのまま)ことがあり、最終データ受信時にNAKを返すようにしたら安定しました。

 実行結果は下記のようになり、1秒間隔で測定値を表示します。途中で値が大きく変わっているのは磁石を近づけてみたためです。w
 測定値は12bit精度でオーバーフローした場合、 -4096 の値になります。(とデータシートにも書いてます)
 静かに置いておいた場合でも測定値が±1程度揺らぎます(環境や回路の組み方にも依存)が、HMC5883L自体は設定により複数の測定値の平均を出力することも可能です(ディフォルトでは平均は取らない)


実行結果
:run
 x =  -69,  y =  116,  z = -357  
 x =  -70,  y =  121,  z = -352
 x =  -71,  y =  120,  z = -358
 x =  -70,  y =  120,  z = -352
 x =  -69,  y =  117,  z = -351
 x =  -72,  y =  120,  z = -354
 x =  -70,  y =  120,  z = -350
 x =  -70,  y =  123,  z = -355
 x =  -69,  y =  121,  z = -357
 x =  -73,  y =  119,  z = -352
 x =  -76,  y =  364,  z = -255
 x = -209,  y = 1193,  z =   55
 x = -335,  y = 1267,  z =  229
 x = -441,  y = 1471,  z =  579
 x = -706,  y = 1584,  z =  924
 x =-1235,  y = 1741,  z = 1514
 x =-1492,  y = 1728,  z = 1911
 x =-1331,  y =-4096,  z = 1658
 x =-1842,  y =-4096,  z = 1399
 x =-1778,  y =-4096,  z = 1416
 x =  -77,  y =  135,  z = -363
 x =  -70,  y =  134,  z = -363
 x =  -72,  y =  144,  z = -360
 x =  -69,  y =  138,  z = -361
 x =  -69,  y =  137,  z = -367


★追記 2016/05/07
 250ms 毎にサンプリングしながら、ブレッドボードを手で1回転して採取したデータを3Dグラフ表示してみました。3Dグラフの描画ツールはリニアングラフ3Dを使用しています。
 3軸磁気センサー基板はLアングルのピンヘッダ(ストレート型をラジオペンチで直角に曲げたw)を使いブレッドボートに立てて(シルク印刷のYが鉛直方向)、机の上で1回転したのでX-Zが円になると予想したのですがX-Yで円が描けました。ノートPCが近くにあったので影響を受けたのかもしれません。
 円の中心が原点(0,0)ではないので電子コンパス的に使うためには測定ポイントを楕円近似して中心点を求めてから方向判定する必要がありそうです。

X-Y-Z Graph X-Y Graph


I2C connect to 3-Axis Digital Compass(picle言語)
# I2C test 3-Axis Digital Compass(HMC5883L)
#  written with picle language by skyriver
#   2016/05/06 ver 0.03

var RegRcv,RegTrn,RegBrg;
var RegCon,RegStat;
var I2CRcv,Msg,_RdData,RdData;

# send data to slave
#  data <- send data
#  return -> 0:no error
func I2CSnd( data ) {
    while ( RegCon[0] & $1f ) {}
    RegTrn[0] = data;
    while ( RegStat[0] & $4000 ) {}
    return = RegStat[0] & $8000;    # check nack
}

# receive data form slave
#  ack <- 0:nak, else:ack
#  I2CRcv -> receive data
#  return -> 0:no error
func I2CRcv( ack ) {
    while ( RegCon[0] & $1f ) {}
    RegCon[0] = RegCon[0] | $0008;  # enable receive
    while ( RegCon[0] & $0008 ) {}
    if ( ack ) {
        RegCon[0] = RegCon[0] & ~$0020; # set ack
    } else {
        RegCon[0] = RegCon[0] | $0020;  # set nack
    }
    RegCon[0] = RegCon[0] | $0010;  # send ack/nack
    while ( RegCon[0] & $0010 ) {}
    I2CRcv = RegRcv[0];
    return = RegStat[0] & $0040;    # check overflow
}

# start seqence and send salave adrs
#  adr_cmd <- slave_adrs << 1 | RD:1
#  return -> 0:no error
func I2CAdr( adr_cmd ) {
    RegCon[0] = RegCon[0] | 1;  # start
    return = I2CSnd( adr_cmd );
}


# stop I2C sequence
proc I2CStop() {
    RegCon[0] = RegCon[0] | $0004;  # set PEN(stop)
    while ( RegCon[0] & $0004 ) {}
}


proc Init( base ) {
    var Ad1pcfg;
    Ad1pcfg = $032c;
    Ad1pcfg[0] = $ffff; # set digital mode
    RegRcv = base;
    RegTrn = base + 2;
    RegBrg = base + 4;
    RegCon = base + 6;
    RegStat = base + 8;
    RegBrg[0] = 157;    # Fcy:16MHz -> 100kHz
    RegCon[0] = $8000;  # enable I2C
    Msg = Array_;
    _RdData = Array_(3);
    RdData = _RdData;
    Msg[0] = " x =";
    Msg[1] = ",  y =";
    Msg[2] = ",  z =";
}


proc main() {
    var adr,err,i,j;
    Init( $0210 );  # I2C 2ch:$0210
    adr = $1e * 2;  # HMC5883L's address

    err = I2CAdr( adr | 0 );
    err = err | I2CSnd( 2 );    # set register No
    err = err | I2CSnd( 0 );    # continuously messure
    I2CStop();
    if ( err ) {
        PrnStr_( "error !" );
        exit();
    }

    for ( j = 0; j < 60; j=j+1 ) {
        while ( Timer_ ) {}
        Timer_ = 100;
        err = I2CAdr( adr | 0 );
        err = err | I2CSnd( 3 );    # set read start register
        if ( err ) {
            PrnStr_( "err" );
        }
        else if ( I2CAdr( adr | 1 ) ) { # if read cmd err
            PrnStr_( "read cmd err" );
        } else {
            for ( i = 0; i < 2*3; i=i+1 ) {
                if ( I2CRcv( i < (2*3-1) ) ) {
                    PrnStr_( "¥nerr i=" );
                    PrnDec_( i );
                    break;
                }
                _RdData[ i ^ 1 ] = I2CRcv;  # exchange high<->low byte
            }
        }
        I2CStop();
        for ( i = 0; i < 3; i=i+1 ) {
            PrnStr_( Msg[ i ] );
            PrnDecF_( RdData[ i ], 5 );
        }
        PrnStr_( "¥n" );
    }
}


[ 前へ ] 連載記事 [ 次へ ]
nice!(0)  コメント(0)  トラックバック(3) 
共通テーマ:趣味・カルチャー

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 3