データロガーの制作

  1. データロガー

    1. データロガーとは
      データロガーはデータを収集・保存する道具です。「アメダス」などの気象関連のデータ収集機器もデータロガーの仲間です。秒あたり千個以上の高速なデータ記録から、1時間単位のデータまで、記録間隔は対象により異なります。
       データ対象も、温度、圧力、歩数、などいろいろです。

    2. 構成
       データ入力、日付時刻情報、データ記録、が主な機能です。ここでは、PICを用いたAD入力、RTCによる日付・時刻、シリアルメモリによる簡易小型データロガーを制作します。
       温度、湿度、気圧、などの気象情報の記録を目的にします。

  2. 設計
    1. ブロック設計
       全体のブロック設計です。データ入力は、アナログ信号の前処理になります。接続はアナログ信号になります。RTCとシリアルメモリはI2C接続になります。RTCからは、測定間隔の割り込み信号を取得します。PICのタイマーを測定間隔にすると、時刻の値と測定時刻がずれる心配があります。
       RS232CはPCとの接続に利用し、各種設定や収集データのPCへの移動を行います。



      各接続回路は別項目で紹介した回路を利用します。

    2. 詳細回路
       センサー部を除く回路は次のようになります。右上は液晶表示は、PCから制御する場合は不要です。PIC右下はRS232CドライバでPCとの接続に利用します。シリアルメモリをはずして、別の回路で読むようにすればこのドライバは不要です。12ピンR1Oに接続するスイッチはマイクロローダでプログラムを書き込む場合、必要です。
       PIC下のメモリと時計はI2C接続です。メモリは増設可能ですが、1Mbitを利用する場合最大2個しか接続できません。RTC(リアルタイムクロック)のINTはRTCタイマーからの割り込み信号です。
       PIC左の発振子は20MHzを利用していますが、消費電力を考えると、4MHzでも十分です。PICの A0,A1にはセンサーのアナログ回路を接続します。雑音を考慮すると、別基板にするほうがよいでしょう。



       各回路は別項目で紹介していますが、接続するピン番号が異なる場合があります。

    3. ソフト設計(PIC)
       データの記録は、RTCからの信号をRB0に接続し、タイマーからの信号でデータを計測し、記録します。 
      記録形式は

       年月日、時刻、データ1、、 データn

      の形式で、これを1記事として外部フラッシュメモリに書き込みます。フラッシュメモリの書き込み時間は10mS程度なので、高速な記録には適当でありません。
      また、PCからのデータ要求を int_rda 割り込みで受け取り、時刻の設定や記録データの送信を行います。

    4. ソフト設計(PC)
       PC側(ホスト)からPICに対し、時刻・設定情報の送付、記録データの取得、などのサービスが必要です。
      時刻設定は以下の形式で送ります。

      T Y2Y1M2M1D2D1h2h1

      ここで、Y2Y1 は年度の下位2桁、M2M1は2桁の月、D2D1は日にち、h2h1m2m1s2s1 は各2桁の時分秒のデータです。

      ログの設定はアナログポート数(an)、サンプル間隔(int)、サンプル数(sn)、を設定します。数は2バイトの正の整数の範囲とします。

      L an,sn5,sn4,int3,int2,int1,sn5,sn4,sn3,sn2,sn1

      データの受信は G コマンドで起動します。Gコマンドを受け取ったPICは記録済みのデータを1サンプル1行で送ります。データが尽きたら、Y2Y1を 99 として返します。d2d1はセンサーからのデータです。<LF>は改行コードです。

      G
      Y2Y1M2M1D2D1h2h1d2d1d2d1.......<LF>
      Y2Y1M2M1D2D1h2h1d2d1d2d1.......<LF>
      99M2M1D2D1h2h1d2d1d2d1.......<LF>

      最後のY2が99のデータは、データが尽きたことを示します。

  3. PIC側プログラム

    1. シリアル記憶なし
       簡単のため、シリアルメモリへの記憶をせず、4桁のLEDにより温度と時刻の表示を行うプログラムを紹介します。プログラムは RTCからのタイマーで割り込み処理を行う関数 void ext_isr() 、外部シリアルからの信号で割り込み処理を行う関数 void rda_isr()、と main() から構成されます。
       時刻や測定間隔は、PC のソフトを RS232Cで接続して行います。rtc.h はこちらにあります。

      #include <16f873.h>
      
      //#fuses INTRC_IO,NOWDT,NOLVP,NOMCLR //内部クロック、WDT,LVPなし
      #fuses HS,NOWDT,NOLVP,put,brownout //外部クロック、WDT,LVPなし
      #device ADC=10
      #use delay(CLOCK=20000000)
      #use RS232(BAUD=4800,xmit=PIN_C6,rcv=PIN_C7)//use delayの後に配置する
      #use i2c(MASTER, SDA=PIN_C4, SCL=PIN_C3, FORCE_HW) // I2C使用宣言
      
      #include <rtc.h>//rtc用の関数読み込み
      
      //int count;
      float aval;
      float tmp;
      float tmax,tmin;
      long ltemp;
      int num;
      int prd;
      int idle;
      
      int rtime;
      int digit;
      long val;
      int ttmp;
      
      long wadrs,radrs;
      char cm[15],ch;
      
      void writeLog(int ca,long ad);
      void readLog(int ca,long ad);
      
      //rtc timer 
      #INT_EXT
      void ext_isr(){
              
          rtc_date_read();//時刻を取得
          output_low(PIN_A4);
          delay_ms(10);
          output_high(PIN_A4);        
          
          aval=read_adc(); //温度センサーを読む    
          //lcd_clear();
          //printf(lcd_data,"av:%3.1f\n\r",aval);
      
           //測定 389;5  163:49 
              tmp=-0.195*aval + 80.7;//温度に変換
             
          //最高と最低温度を更新
          if(tmax < tmp) tmax = tmp;
          if(tmin > tmp) tmin = tmp;
      
          //温度をシリアル送信
          printf("%c%c_%c%c_%c%c:%c%c:%c%c:",
                h_month,l_month,h_day,l_day,h_hour,l_hour,h_min,l_min,h_sec,l_sec);
          printf(" tmp=%4.1f;",tmp);
          
          //温度と時刻を記録
          //writeLog(0xA0,wadrs);
          //wadrs += 12;
             
      }
      
      #INT_RDA
      void rda_isr(){
         char ch;
         idle=0;
         num=0;
         
         //シリアル通信から設定
         //コマンドを読む
         ch=' ';   
         do{
              ch=getch();
           putchar(ch);
           cm[num++]=ch;
         } while (ch != ';');
         //printf("piccom:%c:%d\n\r",ch,num);
         
        //日付の設定
         if(cm[0]=='D'){//日付   
              num=0;
          year = (cm[1]-0x30)*16+(cm[2]-0x30);
          month = (cm[3]-0x30)*16+(cm[4]-0x30);
          day = (cm[5]-0x30)*16+(cm[6]-0x30);
          //week =(cm[7]-0x30);
          hour = (cm[7]-0x30)*16+(cm[8]-0x30);
          min = (cm[9]-0x30)*16+(cm[10]-0x30);
          sec = (cm[11]-0x30)*16+(cm[12]-0x30);
          rtc_date_set();
        }
        //測定間隔の設定
        else if(cm[0]=='I'){//set interval
          num=0;
          prd = (cm[1]-0x30)*10+(cm[2]-0x30);
          //printf("Pic:Intval:%d\n\r",prd);
          rtc_timer_set(prd);//タイマーの値
          rtc_timer_start(0x82);//更新間隔    
        }
        //記録データの受信
        else if(ch == 'g') {
              //readLog(0xA0,radrs)   ;
           //radrs += 12;
        }
      }
              
      main(){
        //char cmnd;
      
        //rtcが安定するのを待つ
        delay_ms(500);
        
        //初期設定
        num=0;
        wadrs=0;
        radrs=0;
        
        rtime=200;
        ttmp=0;
         
        output_float(PIN_C3);         //SCLピン定義
        output_float(PIN_C4);         //SDAピン定義
        //日付の仮設定
        year=0x06; month=0x07; //データは16進2入力
        week=0x3; day=0x11; 
        hour=0x22; min=0x40; 
        sec=0x00;
        rtc_control_init();
        rtc_date_set();
       
       //rtcのタイマー設定
        rtc_clockout_set(0x83);//set Clockout 1sec
        //ログ間隔(秒)設定
        rtc_timer_set(5);//Interval sec  
        rtc_control_set(0x11);//set Timer &Enable
        rtc_timer_start(0x82);//TimerStart
        ext_int_edge(H_TO_L);
        
        //AD入力の設定
        setup_adc_ports(RA0_ANALOG);//アナログ入力設定
        setup_adc(ADC_CLOCK_DIV_32);//クロック設定
        set_adc_channel(0);//チャンネル0切り替
        
        delay_us(10);//10μ秒待つ
        tmax=-10.0;tmin=1000.0;
          
        //受信済みのデータを捨てる  
        while(kbhit()) {
                ch=getchar();
                putchar(ch);
        }
         
        printf("start log;");
       //割り込みの設定     
        enable_interrupts(INT_EXT);
        enable_interrupts(INT_RDA);
        enable_interrupts(GLOBAL);
      
        //測定開始
        while(1){
         
         if(rtime==0) {//表示の更新
                 if(ttmp ){//温度の表示更新                     
                   val=(long)(tmp*10.0);         
               st[3]=val/1000;
               val -= st[3]*1000;
               st[2]=val/100;
               val -= st[2]*100;
               st[1]=val/10;
               st[0]=val%10;
               ttmp=0;
             }
             else{
                  //時刻表示更新
                      rtc_date_read();
                  st[3]=h_hour-0x30;
              st[2]=l_hour-0x30;
              st[1]=h_min-0x30;
              st[0]=l_min-0x30;
              ttmp=1;
             }           
                 rtime=200;
         }
         
         rtime--;   
         //4桁表示器に表示
         for(digit=0;digit<4;digit++){
                 output_sr( segment_data[st[digit]]);
                 output_dgt(digit,0);
                 delay_ms (3);        //表示期間(ミリ秒)
                 output_dgt(digit,1);
         }   
        }
        
        return 0;
      }

      ext_isr() では、PINA4に短いパルスを送ります。これは動作の確認用で、測定処理とは無関係です。read_adc(); でセンサーの信号を読み、計算式で温度に変換します。この部分は、温度センサーの項を参照してください。計算式はセンサーや抵抗値で変化します。最後に、最低と最高温度を表示し、測定の日時と温度をシリアルに送信します。

      void rda_isr() は、PCからの日付や測定間隔の設定情報を読み取る部分です。PCからの信号で割り込み処理を行います。最初に、';' を受信するまで、PCからの信号を読みます。最初この部分を1文字単位で割り込むようにプログラムをしましたが、割り込み処理が遅く受信に失敗する場合があり、最初の一文字のみ割り込みとし、割り込み処理の中で続く文字を読み取るよう、変更しました。
       日付は DYYMMddmmss; の形式で受信します。先頭は日付を示す記号で、YY以下が、年月日時分秒 各2桁の文字で構成されます。これを、BCD符号(16進)に変換して、RTCに設定します。
       温度のサンプル間隔は Itt; の形式です。先頭が間隔を示す記号、tt は間隔です。RTCへのタイマー指定は 10進数です。設定は二桁ですから99秒が最大です。RTCには分単位で設定することも可能です。
       main() 関数は、RTCの初期設定後、時刻と温度を交互に4桁の表示器に表示します。LCDを利用する場、月日や最高、最低温度も表示できます。


    2. シリアルメモリへの読み書き
       未完

  4. PC側プログラム

    1. ダイアログ形式
       PC側は visualC6.0 によるダイアログ(MFC)でプログラムを作成しました。実行画面を示します。日付時刻は PC側の現在の時刻を送信ボタンで送信します。記録間隔は数字を設定後送信ボタンで送信します。
       データ受信は、ここでは省略します。表示窓では、測定時刻と温度を表示します。測定回路の電源を安定化していないため、測定値がばらついています。



    2. CRS232Cクラス
       シリアルの送受信は、CRs232C クラスで行っています。まず、RsOpen() でシリアル送受信を行うデバイスを開きます。

      int CRs232C::InitCommPort(DWORD dwEvtMask,DWORD RxBuffSize,DWORD TxBuffSize)

      で、マスクやバッファサイズを設定し、

      int CRs232C::SetCommPort(UINT BaudRates, BYTE DataBits, int StopBit,
       int Parity, bool CtsFlow, bool XparameterFlow)

      で、RS232Cのパラメータを設定します。受信は

      CString CRs232C::RsRcv() で、

      送信は

      void CRs232C::RsSend(CString sendBuff)

      で行います。受信はイベント機能を持っていませんから、ターマー処理で定期的に受信処理の有無をチェックする必要があります。

    3. class CLoggerDlg クラス
       ダイアログの処理を行うクラスです。

      BOOL CLoggerDlg::OnInitDialog() は初期設定を行います。ここで、CRs232C クラスのインスタンスをRSとして作成しています。

      void CLoggerDlg::OnButtonDate() は日付ボタンを押した場合の処理で、CTime::GetCurrentTime(); で現在のPCの日付、時刻を取得し、これを、PICに送ります。

      void CLoggerDlg::OnButtonIntvl()  は サンプル間隔を設定する関数で、設定時間から
       Imm;
      のコマンドをPICに送ります。最後の ; がコマンドの最後を示します。

      void CLoggerDlg::OnTimer(UINT nIDEvent)  は InitDialog() で設定されるタイマー処理関数で、シリアルからの受信をチェックします。;  を含む受信文字があれば、最後に、改行のための \r\n を付加します。また、指定した行より長くなった場合、先頭に1行を削除後、受信した行を表示します。

    4. 組み込み
       このプロジェクトはここにあります。解凍後 logger.exe をクリックして起動します。ソースなしの実行形式(自己解凍形式)はこちらにあります。ダウンロード後、ダブルクリックで解凍します。