周波数の測定

  1. 計数と割り込み
     ここでは、16ビットの「タイマー1」を利用して、周波数カウンタ(秒当たりのパルス数を計測する回路)を作成します。1秒の計測には、タイマー0の割り込みを利用し、表示には先に紹介した、7素子動的表示回路を利用します。
     7素子表示回路の詳細は、こちらを参考にしてください。

  2. 回路設計

    1. タイマー1
       タイマー1は16bitのカウンタとして利用できます。また、3ビットのプリスケーラを利用できるので、最大19ビットのカウンタとして利用できます。タイマー1をカウンタとして利用するには、RB6 を計数用の外部信号として「利用します。プリスケーラを利用しない場合、以下の設定をします。

      setup_timer_1(T1_EXTERNAL_SYNC | T1_DIV_BY_1);

      T1_EXTERNAL_SYNC は外部信号を入力として利用することを指定します。T1_DIV_BY_1 は外部信号を1で割る(プリスケーラを使用しない)ことを意味します。
       タイマーの値を初期化するには、

      set_timer1(val)

      を利用します。val は16ビットの設定値です。タイマー1で計数した値は

      lcount=get_timer1();

      で読み出します。lcountは16bit変数とします。


    2. 回路設計
       カウンタの入力は RB6/ の端子になっています。したがって、Bポートを 7素子表示器への出力として利用する場合、連続した7本の線が利用できません。
       ここでは、RB5..RB0 と RB7 の端子を、7素子表示器へのデータ信号として利用します。7素子の各素子をRB1,RB2,..,RB7に接続します。RA0,1,2 でトランジスタを利用し、表示する7素子の桁を切替えます。



      プログラムで、1秒間countの信号を計数し、その値を7素子表示器で表示します。3桁の周波数測定ができます。1秒で999以上のパルスの場合、計数時間を 0.1秒、0.01 秒と短くします。逆に、計数信号が少ない場合、計数時間を長くします。

  3. プログラム1:スイッチ割込み

    1. タイマー0割り込み
       ここではタイマー0を割り込みに利用します。タイマー0は8ビットの計数器で、計数値が255を超えると、割り込みを発生することができます。
       タイマー0には計数信号として、内部のクロックを利用します。内部クロックの1/4の周波数がタイマー0への計数信号となります。タイマー0 は8bitのプリスケーラが接続可能で、入力のクロックを最大 1/256 に分周して利用できます。
       ここでは、1秒の計数ができればよいので、次のようにタイマー0を設定します。

      setup_counters(RTCC_INTERNAL , RTCC_DIV_64);

      RTCC_INTERNALは内部クロックを入力として利用すること、RTCC_DIV_64 は入力を 1/64 に分周して利用することを指定します。

    2. 割り込みプログラム
       タイマー0を割り込みとして利用するには、次のように関数を呼び出します。

       enable_interrupts(INT_TIMER0);
       enable_interrupts(GLOBAL);

      enable_interrupts(INT_TIMER0); は、タイマー0の割込みを許可することを意味します。 enable_interrupts(GLOBAL); は割り込み機能そのものを利用することを許可します。これをしないと、タイマー0割り込みが発生しても、割り込みは起こりません。割り込みが起こると、以下のように指定された関数(割り込み処理プログラム)を実行します。

      #int_rtcc
      void rtcc_isr()
       //タイマー0割り込み処理
      }

      先頭の #int_rtcc は、タイマー0による割り込み処理関数が始まることを指示します。int_rtcc() がタイマー0 による割り込み処理関数の名前です。これだけのプログラムで、タイマー0の計数値がオーバーフローすると、現在実行中のプログラムを中断し、int_rtcc() 関数を実行します。この関数が終了すると、先に中断した時点からプログラムが再開されます。

       関数呼び出しには、ハードウエアによるスタック処理が必要です。このスタックは4段までしかありませんから、割り込みを含めた関数の多重呼び出し(関数の中で関数を呼ぶ)は4段に制限されます。

    3. 1秒の計測
       時間はタイマー0の割り込みを利用します。ここでは、4MHzの内部発振を利用します。したがって、タイマー0 への計数信号の周波数は1MHzです(ということは、周期は1マイクロ秒です)。タイマー0はプリスケーラで 入力を 1/64 に分周し、この信号を255クロック受けるとオーバーフローします。したがって、
       1*64*255 = 16384 μ秒 間隔でオーバーフローし、割り込みを発生します。したがって、1000/16.384 =61.0 回 の割り込みが起こると、ちょうど1秒になります。 以下のプログラムでは、この値を変数 nm に設定しています。

    4. ソース
      以下に、タイマー0を利用した、計数信号の周波数を計数するプログラムを紹介します。
       main()プログラムでは、常に3桁の値を動的表示しています。
      周波数の表示変更はタイマー0割り込みで行います。 mode=0 のときは計数値を表示します。一定時間が経過すると、mode=1 として計数を開始します。
       割り込みプログラムでは、mode=1 で割り込むたびに inm を1減らし、これが0 になると1秒経過したことになりますから、lcount=get_timer1(); で、タイマー1の値を lcount に読み込みます。lcount の値を10進数3桁に変換し、v3,v2,v1 に記録し、mode=0 とします。
       mode=0 では、ns 回の割り込みを計数します。この間、main() プログラムでは、7素子表示器に3桁の値を表示しています。ここでは、ns=76 としていますが、特に意味はありません。ns の値を大きくすると値を表示する期間が長くなり、周波数の測定間隔は短くなります。ns回割り込むと、タイマー1の計数値を0にして、外部計数信号の計数を開始します。

      #include <16f648a.h>
      
      #fuses INTRC_IO,NOWDT,NOLVP,NOMCLR//内部クロック、WDT,LVPなし
      #use delay(CLOCK=4000000) //クロック4MHz
      
      #byte port_a=5 //Aポート番地
      #byte port_b=6 //Bポート番地
      
      //b6 計測信号
      //b0-b5,b7:7素子接続
      //A2,A1,A0:3,2,1桁点灯
      //タイマー0割り込み:0.256mS
      //nm:測定回数
      //ns:休止回数 
      
      int mode;
      //タイマー0をプリスケーラ、1/64で利用
      long nm=61;//1秒 ,1000/(256*64)
      long ns=76;//適当な表示間隔
      long ins,inm;
      
      int v3,v2,v1;
      int digit;
      long lcount;
      long valt;
      
      //int segment_data[]={0x7E,0x0C,0xB6,0x9E,0xCC,0xDA,0xFA,0xE,0xFE,0xCE};
      //intsegment_data[]={0x3f,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67};
      //b5-b0 f:a,b7:g を利用
      int     segment_data[]={0x3f,0x06,0x9B,0x8F,0xA6,0xAAD,0xBD,0x07,0xBF,0xA7};
      
      #int_rtcc
      void rtcc_isr(){//タイマー0割り込み処理
      
              if(mode == 0){//display
                      ins--;
                      if(ins == 0){
                              inm = nm;
                              mode = 1;
                              set_timer1(0);
                              output_bit(PIN_A3,0);
                      }
              }
              else {//count
                 inm--;
                 if(inm == 0){
                    //getcount
                   lcount=get_timer1();
                      
                   v3 = lcount/100;//3桁目
                   valt = lcount % 100;
                   v2 = valt / 10;//2桁目
                   v1 = valt % 10;//1桁目
           
                   mode=0;
                   output_bit(PIN_A3,1);
                   ins=ns;
           }
         }   
      }
      
      void main(){
      
        set_tris_a(0xf0);//Aポート4bit出力(桁指定)
        set_tris_b(0x40);//Bポート下位7bit出力(7素子)
        
        mode = 1;
        inm = nm;
        
        setup_counters(RTCC_INTERNAL , RTCC_DIV_64);//タイマー0設定
        setup_timer_1(T1_EXTERNAL_SYNC | T1_DIV_BY_1);//タイマー1設定
        
        enable_interrupts(INT_TIMER0);//タイマー0割り込み許可
        enable_interrupts(GLOBAL);//割り込み機能許可
      
         lcount = 0;//表示する値
         v3=v2=v1=0;
         digit = 1;//桁数
      
         while(1){
      
            if(digit== 3){//第3桁の表示
              port_b = segment_data[v3];
              output_bit(PIN_A2,1);//3桁表示開始
              delay_ms (5);        //表示期間(ミリ秒)
              output_bit(PIN_A2,0);//表示を消す
              delay_us(5);
            }
      
            if(digit== 2){
              port_b = segment_data[v2];
              output_bit(PIN_A1,1);
              delay_ms (5);
              output_bit(PIN_A1,0);
              delay_us(5);
            }
      
            if(digit == 1){
              port_b = segment_data[v1];
              output_bit(PIN_A0,1);
              delay_ms (5);
              output_bit(PIN_A0,0);
              delay_us(5);
            }
            digit ++;//表示桁を変更
            if(digit ==4 ) digit=1;
        }
      }

    5. ゲート信号と外部計数信号
      上のプログラムでは、RA3 を外部信号を計数し、表示を開始すると点灯するLEDを接続することができます。回路の動作確認にも便利です。
       周波数を測定する外部計数信号は、TTL型の論理信号であることが必要です。

  4. 課題

     
    1. 測定周期の変更
      4桁の小型スイッチをつけ、スイッチにより周波数の測定間隔を、1秒、0.1秒、0.01秒、1m秒 に変更できる周波数カウンタを設計しなさい。