タイマー割り込みの使いかた

平成20年8月17日

タイマー割り込みを使ったサンプルプログラム

    付録CD-ROMに収録されている、timer、dac、OTO_PRO1などのプログラムがタイマー割り込みを使用しています。

タイマー割り込みを使う方法

    タイマー割り込みを使うには、割り込み処理関数を登録するだけではなく、割り込み許可レジスタもセットする必要があります。


            trg_regintsrv(TRG_INTID_BSITM000,intsrv_tim); // 割り込みハンドラ登録
            PRM00 = 1;
            CR000 = 0x100;  // 割り込み周期
            TMC00 = 0x0C;   // カウント開始
            MK0H.6 = 0;     // TM000割り込み許可
     

    上のプログラムで、

      PRM00 = 1;
      CR000 = 0x100;  // 割り込み周期
      TMC00 = 0x0C;   // カウント開始

    の3行は、内蔵周辺ペリフェラルのタイマー00の設定です。

    MK0H.6は、TM000割り込みを許可するための割り込みマスク・フラグ・レジスタです。このレジスタはデフォルトではすべて1になっていて、0を書き込むことで割り込みが許可されます。

    詳しくは、78F0730のユーザーズマニュアルの421ページを参照してください。

割り込み処理関数ではフラグレジスタをクリアする

    割り込み処理関数内では、割り込みで処理すべき内容を処理した後、割り込み要求フラグ・レジスタをクリアしてください。

    詳しくは、78F0730のユーザーズマニュアルの419ページを参照してください。

 

タイマー関連レジスタの設定方法

    78F0730で一番よく使われる内蔵周辺ペリフェラルは、おそらく「16ビットタイマー00」だと思います。
    ここでは、周期的な割り込み発生を行う方法を簡単に説明します。

    最低限設定すべきレジスタは、PRM00と、CRC000と、TMC00の3つです。あとのレジスタは設定しなくても何とかなります。

    PRM00は、タイマーカウントに使うクロックの選択を行います。PRM00=1とすると4MHzで、PRM00=1とすると16MHzでカウントされます。

img1.png

    CR000はタイマーの周期を選択します。

    PRM00=1とした場合は4MHzでのカウントとなるので、CR000=0x100にセットした場合は4000000/256 = 15625Hzでイベントが発生します。

    img1.png

     TMC00を0x0Cにセットすることで、カウント動作が開始されます。

    img1.png

     

    簡単に言えばPRM00=1の場合、割り込み周期 = (CR000の値) * 0.25 [μs]です。

タイマー割り込み周期の精度は

    CR000の値を4000にセットすれば、1ms周期での割り込みとなるはずですが、実際には揺らいでいます。

    たとえば、次のようなプログラムを作った場合を考えます。これはメインのループの中でタイマー割り込みを使って動作するようなプログラムであって、USBから1文字受信したらループを抜けるようなプログラムです。


          while(1) {

      /* ここでタイマー割り込み処理が発生している */

              if(trg_getrecvcount()) // 何か受信データがある場合
      {
               char c = trg_getc(); // 1文字受信
               if(c == 'x') break; // 'x'ならばループを抜ける
      }
         }

    タイマー割り込みが発生する時間には、5μ秒程度のゆらぎがありました。

    img1.png

    この原因は、trg_getrecvcount()関数にあります。

    trg_getrecvcount()関数はUSBファームウェアの関数を呼ぶのですが、USBファームウェアの関数は内部でDIやEIを使っています。DIされている期間にタイマーが入っても応答することができないので、タイマー割り込みの発生時刻がゆらぐのだと思われます。

    それならば、次のようにメインのループではUSB関係のルーチンは一切コールしないようにしてみました。

    unsigned short gCount; // 終了判定カウンタ

    void intsrv_tim(void) {
    __asm("push de");         gCount++;
    P6.1 ^= 1; // LEDをチカチカ
            IF0H.6 = 0; // タイマー割り込みフラグをクリア
    __asm("pop de"); }

    void main() {

            gCount = 0;

    ・・・・
    /* 各種初期設定 */
    ・・・・

    trg_regintsrv(TRG_INTID_BSITM000,intsrv_tim);

    MK0H.6 = 0;     // TM000割り込み許可
    while(gCount < 10000) {

           /* ここでタイマー割り込み処理が発生している */
    }

    }

    この場合、タイマー割り込みの発生時刻のゆらぎは1μ秒ほどになりました。

    それなりに正確な周期で割り込みを発生させようとするならば、だいたい12〜16μ秒ならば限界になるようです。もちろん、複雑な処理をすればするほど長くなります。

    最短にしたい場合でも、32μ秒くらいにしておくのがお勧めです。

    img1.png

まとめ

    @ タイマー割り込みの設定方法は、

      PRM00 = 1;
      CR000  = (割り込み周期[μs] * 4);
      TMC00 = 0x0C;   // カウント開始
      MK0H.6 = 0;       

      trg_regintsrv(TRG_INTID_BSITM000,intsrv_tim);

    A USBの関数を呼び出すと、割り込み周期が乱れる(5〜50μ秒くらい)

    B 正確な割り込み発生周期の限界は、だいたい12〜16μ秒

    C USB関数を呼び出さなくても、USBから何か受信するだけで周期が乱れることがある。それが問題になる場合は、 trg_usbclose(); 関数を呼んでUSBを一切無効にしてみると良い。

    D 割り込みルーチンの先頭と最後で、DEレジスタのPUSH POPを忘れずにする。

     

 

chickens_back.gif 戻る

 Copyright(C) 2008 NAITOU Ryuji. All rights reserved. 無断転載を禁ず