方位センサー

  1. 方位センサ

    1. 方位センサモジュール

      これは、日立の HM55B を利用した地磁気を感知する方位センサーです。モジュールは 
        http://www.microbot-ed.com/j_digital_compass.html
      で扱っています()。



      HM55B は、直交した磁気センサを二つ組み込み、X,Y 方向の地磁気の強さを11ビットのシリアル信号で出力します。測定時間は30mS、測定範囲は±180μT(T:テスラ) 、磁気感度は1μTです。動作電圧は3V、測定時の電流は10mAです。
       モジュールは6ピンのDIPで各端子を次の表にまとめます。DIN、DOUT はCLKに同期してデータを送り/取得します。図で左下(手前)が1ピンになります。
      ピン番号 端子名 意味
      1 DIN コマンド入力
      2 DOUT フラグ、データ出力
      3 GND 電源グランド
      4 CLK クロック信号
      5 CSB 選択信号(0Vで選択)
      6 VCC 3V(5V?)

    2. コマンド

       測定を行うには、コマンドを利用します。次のコマンドを利用します。
      符号 意味
      0000 リセット
      1000 測定開始
      1100 読み取り

      最初にリセットを行います。次に、測定開始コマンドを送ると計測を開始し、30mS〜40mS後に測定結果を読み取ることができます。読み取りデータは最初に 終了フラグ2ビット、エラーフラグ2ビット、に続けて、各11ビットのX,Y データが送られます。X,Y データはセンサーに対する地磁気のX、Y 成分です。センサーの長手方向が東西方向のとき、

    3. シリアルデータ

       クロック信号の立下りに同期してDINにコマンドを送ります。下記は、測定開始コマンドのタイムチャートです。



      読み取りコマンドを送ると、クロック(CLK)の立下りに応じて、終了フラグとエラーフラグが返ります。終了フラグが 11 の場合、測定が完了したことを意味します。


      続けて、クロックを送ると、X,Y のデータをDOUT信号から取得できます。X,Y のデータは地磁気に対するセンサーのX,Y成分です。地磁気の南北方向とセンサーの長手方向が一致すると、Y成分が0、X成分が最大になります。


          図は、マニュアルより引用

  2. 回路設計

    1. 回路設計

      携帯しやすいように、電池駆動にします。センサーモジュールと16F88を以下のように接続します。出力はLEDを利用します。計測をが完了したとき、X成分、Y成分がほぼ0になったとき(東西、南北方向)発光します。

    2. 回路



  3. プログラム

    1. プログラム

       #define で 接続する端子(PIN_B2)を、センサーの端子名(CLK)で置き換えます。reset(); start(); はリセットおよび測定開始コマンドを送る関数です。共に sendCmnd(int cmnd) で、cmnd の下位4ビットをコマンドとしてモジュールに送ります。
       checkBusy(); は、計測が完了したかを判断する関数です。読み取りコマンドを送り、4ビットのフラグを取得します。先頭4ビットが1100 であれば、計測が完了したことになります。readxy( ) で、X,Y 成分を dx, dy に読み取ります。dx,dy の値が十分小さいときLEDを点灯し、東西または南北方向であることを示します。
       X,Y データは11ビットの補数表現ですから第10ビットが符号ビットになります。C言語の2バイトの整数で表現する場合、第10ビットが1のとき11〜15ビットを1にする必要があります。
       if(bit_test(*dx,10)) *dx |= negbits;
      はこのための処理です。

    2. ソース


      //compass module HM55B
      //
      
      #include <16f88.h>
      #fuses INTRC_IO,NOWDT,NOLVP,NOMCLR,NOBROWNOUT
      
      #use delay(CLOCK = 4000000)
      #use fast_io(B)
      
      #define CLK PIN_B2
      #define CSB PIN_B3
      #define DOUT PIN_B4
      #define DIN PIN_B1
      #define negbits  0xf800
      
      void reset();
      void start();
      void sendCmnd(int cmd);
      int checkBusy();
      void readxy(signed long *dx,signed long *dy);
      int read4();
      signed long read11();
      
      signed long dx,dy,val2;
      int i;
      int val;
      
      void main()
      {
         set_tris_b(0xF1);
         set_tris_a(0xF0);
         
         delay_ms(50);//電源の立ち上がりを待つ
         
         reset();//リセット
         
         while(1){
                 
          start();//計測開始
              
          while(!checkBusy()){}//計測完了を待つ
                  
          dx=0;dy=0;  
          readxy(&dx,&dy);//dx,dyに計測値を取得
          
         output_low(PIN_A1);
         output_low(PIN_A0);
          if(dx<2 && dx>-2) output_high(PIN_A1);//Xの値が小さいとき
          if(dy<2 && dy>-2) output_high(PIN_A0);//Yの値が小さいとき
          
          delay_ms(500);//休止
          output_low(PIN_A2);
          delay_ms(500);
         }
      }
      
      void reset()
      {
              output_low(CSB);//モジュールを選択 
              sendCmnd(0x0);//リセットコマンドを送る
              output_high(CSB); //モジュールを選択を解除
      }
      
      void start()
      {
              output_low(CSB); 
              sendCmnd(0x8);//測定開始コマンドを送る
              output_high(CSB); 
      }
      
      int checkBusy()
      {
              output_low(CSB); 
              sendCmnd(0xC);//読み取りコマンドを送る
              val=read4(); //フラグを読む 
              output_high(CSB);
              
              if(val==0xc) output_high(PIN_A2);//測定完了チェック
              else output_low(PIN_A2);
          
          return (val==0xc);//測定完了したらtrueを返す  
      }
      
      int read4()
      {
         //フラグを読む
         val=0;
         for(i=0;i<4;i++){
               output_high(CLK);
               output_low(CLK);
               //
               if(input(DOUT)) bit_set(val,3-i);//上位ビットが先頭
         }
      
          return val;  
      }
      
      void readxy(signed long *dx,signed long *dy)
      {
              //X,Y のデータを取得
              output_low(CSB); 
              sendCmnd(0xC);
              val=read4();
              *dx = read11();//Xデータを読む
              *dy = read11();//Yデータを読む
              
              //負の数なら、16bitの補数にする
              if(bit_test(*dx,10)) *dx |=  negbits;
              if(bit_test(*dy,10)) *dy |= negbits;
              
              output_high(CSB);
              return;
              
      }
      
      //12ビットデータを読む
      signed long read11()
      {
        //クロックを送り、11ビットのデータを読む
        val2=0;
         for(i=0;i<11;i++){
               output_high(CLK);
               output_low(CLK);
               //上位ビットからくる???
               if(input(DOUT)) bit_set(val2,10-i);
         }
          return val2;         
      }
      
      void sendCmnd(int cmnd)
      {
         
         //クロックに同期してコマンドを送る
         for(i=0;i<4;i++){
           output_high(CLK);
           output_bit(DIN,bit_test(cmnd,3-i));
           output_low(CLK);
           
         }    
      }