割り込み処理

  1. 割り込み処理

    1. 割り込みとは

       割り込みは、ハードウエアの要因で、対応する関数を実行する機能です。Windowsのプログラムにおけるイベント処理はこの割り込み機能を拡張した概念です。
       割り込み処理を利用することで、main() での処理とは別の処理を並行して実行することができます。たとえば、動的な表示をしながら、シリアルからデータを受信し表示を行うことが可能になります。

    2. 割り込みのプログラム

       PICには、特定の条件が発生すると、指定した処理関数を実行させる機能ー割り込みといいますーが利用できます。たとえば、タイマー0の計数が255を超えると0に戻りますが、このときに割り込みを発生させることができます。割り込みを行うには、個別の割り込み許可と、全体の割り込み双方の許可が必要です。以下に例を示します。

       enable_interrupts(RTCC_ZERO);
       enable_interrupts(GLOBAL);

       最初が、タイマー0の割り込み許可、次の GLOBAL が全体割り込み許可です。許可された割り込み条件が成立すると、割り込み関数を実行します。

      #INT_RTCC
      rtcc_int(){   }

      最初の #INT_RTCC が、次の関数が 割り込み処理関数 であることを指示します。次の、rtcc_int() が割り込み処理関数です。タイマー0が0に戻るごとに、この関数が呼び出されます。タイマー1の場合、
       enable_interrupts(INT_TIMER1);
      で許可を行い、割り込み関数の指示として
       #int_timer1
      を用います。

  2. タイマー割込み

    1. タイマ0割り込み

       割り込み処理を行うには、プリプロセッサで使用する割り込みタイプを宣言します。以下の例ではタイマ0を使用します。割り込み処理関数は、タイプ名_isr() の名前の関数です。
       main( )関数では、タイマー0の設定で、内部クロックの利用と、8bitプリスケーラの利用を指定しています。enable_interrupts(type)で、割り込みを許可するタイプ(レベル)を宣言します。割り込み要因と、全体(GLOBAL)の割り込み許可が必要です。
       割り込み許可を取り消すには、disable_interrupts(type) を利用します。

      #device PIC16F84        //PICの指定
      #include <16f84.h>
      
      #int_rtcc           //タイマ0割込みの指定
      rtcc_isr() {          //タイマ0割込み処理関数
       
      }               //ここまでが割込み処理関数
      
      main() {
       setup_counters(RTCC_INTERNAL,RTCC_DIV_256);  //タイマ0のモード設定
       
       enable_interrupts(RTCC_ZERO);   //タイマ0割込み許可
       enable_interrupts(GLOBAL);      //グローバル割込み許可
       while(1){ ..}             //割込み待ち
      }

       4MHzの内部クロックで、 setup_counters(RTCC_INTERNAL,RTCC_DIV_256); の場合、
       set_timer0(60);
      を行うと、ほぼ、50mS で割込みをします(プリスケーラでタイマー0のクロックは256μS、256μ*(255-60)=50mS)。割込みを続けるには、割り込み処理の中で、再度、set_timer0(60);を行う必要があります。

    2. タイマー1割り込み

       タイマー1は16ビットの設定が可能ですから、細かい時間制御が可能です。

       setup_timer_1(T1_INTERNAL | T1_DIV_BY_4); 
       set_timer1(prd);
      
       enable_interrupts(INT_TIMER1); //タイマ1割込み許可
       enable_interrupts(GLOBAL);

      で最初の割り込みが可能です。タイマー割り込み関数は次のように定義します。
      #int_timer1
      void timer1_isr()
      { 
         set_timer1(prd); //次の割り込み設定
      }


    3. 例 メロディー演奏

      20Mhzのクロックで、音楽を演奏します。タイマーの設定値 prd は設定周波数 f から次のように計算します。0.8 はT1_DIV_BY_4 の場合のタイマー1へのクロックの周期です。

       (65536-prd )*0.8 *2 *10-6 = 1/f
      #include <16f873a.h>
      
      #fuses HS,NOWDT,NOLVP
      #use delay(CLOCK=20000000) //クロック20MHz
      
      int flag;
      
      //音階の周期 ララ#シドド#レレ#ミファファ#ソ ソ#ララ#シド  ド#レレ#ミ
      //           0   2 3    6    5 6       7     8   9 10
      unsigned long onkai[30]=
              {62695,62854,63005, 63147,63281,63407,63527,63639,
              63746,63847,63941,64031,64115,64195,64270,64341,
              64408,64471,64531,64587,64641,64691,64738,64783,
              64825,64865,64903,64938};
      unsigned long prd;
         
      //メロディー ラ シ ドレミファソラ  
      int melody[30]={0,2,3,5,7,8,10,12,14,15,17,19,20,22,24,26,27};
      int i,oto;
      
      
      #int_timer1
      void timer1_isr()
      {
              output_bit(PIN_C0,flag);
              if (flag==1) flag=0; else flag=1;
              set_timer1(prd);
      }
      
      void main(){
            
      setup_timer_1(T1_INTERNAL | T1_DIV_BY_4);
      set_timer1(10);
      
      enable_interrupts(INT_TIMER1); //タイマ0割込み許可
      enable_interrupts(GLOBAL);
      
       while(1){
         for(i=0;i<17;i++){
           
           oto=melody[i];
           prd=onkai[oto];
           set_timer1(prd);
           delay_ms(500);
         }
         disable_interrupts(INT_TIMER1);
         delay_ms(500);
         enable_interrupts(INT_TIMER1);
         set_timer1(65000);
       }
      }

    4. シリアル受信割り込み

      シリアル受信割り込みの例です。873Aを利用し、ハードウエア受信を設定します。#int-rda で受信割り込み関数を定義します。
      ここではハイパーターミナルから受信した文字を返し、さらに、count の値を送ります。ハイパーターミナルの画面は次のようになります。
       #int-rda は受信専用で、送信用の送信可能の割り込みは #INT_TBE で指定します。

      ソース

      #include <16f873A.h>
      #fuses HS,NOWDT,NOLVP,put,brownout //内部クロック、WDT,LVPなし
      #use delay(CLOCK=20000000)
      #use RS232(BAUD=9600,xmit=PIN_C6,rcv=PIN_C7)//use delayの後に配置する
      //#use RS232(BAUD=9600,xmit=PIN_B2,rcv=PIN_B1)//use delayの後に配置する
      
      int count;
      char ch;
      
      #int_rda
      void rda_isr(){
        ch=getchar();
        putchar(ch);
        printf("%d",count);
      }
        
      void main(){
         set_tris_b(0x0b);
         count=0;
           
         printf("\n\rHello");
           
         //enable_interrupts(int_ext);
         enable_interrupts(INT_RDA);
         enable_interrupts(GLOBAL);
      
         while(1){
              count++;
              delay_ms(500);
         }
      
      }

  3. その他の割り込み要因

    1. 端子割り込み

       INT_EXT は PIN_B0 に接続した信号により割込みます。 信号のエッジ(立ち上がり、または立下り)で割り込み、エッジは
         ext_int_edge( H_TO_L );
      で選択できます。
       また、INT_RB  は RB7..RG4 に接続した信号の変化により割り込みます。

    2. タイマー

      INT_RTCC: タイマー0
      INT_TIMER1, INT_TIMER2: タイマー1,2
      INT_CCP1, INT_CCP2:    キャプチャ割り込み

    3. 入出力

      INT_AD :   AD変換完了
      INT_EEROM :EEROM書き込み完了
      INT_SSP:  シリアル通信割り込み
      INT_TBE, INT_RDA :シリアル通信割り込み