多重割り込みプログラム(多重タイマ割り込みプログラム)

Copyright(C) 19June2004 coskx TNCT

1.はじめに

2つのタイマ割り込みを同時に行ない,2つのLEDをそれぞれ固有の周期で動作させることを考えます。
次の動作を行なうプログラムを作ります。
1つはITUch0を用いて100μsの割り込みを行います。もう1つはITUch1を用いて5000μsの割り込みを行います。
そしてITUch0の割り込みルーチンは819200μs周期, ITUch1の割り込みルーチンは1280000μs周期でLEDを点滅させます。
ITUch0の割り込み周期が短いので,ITUch1の割り込み中にも,ITUch0の割り込みを受け付けるところがこのテーマの大事な点です。

ファイルのダウンロード

DownLoad

2.割り込みの動作の確認

多重割り込みは,単一割り込みの時には起こらなかったことが起こります。割り込み動作の様子を見てみましょう。
特にタイマ割り込み中に「CCRのIのクリア」と「割り込み要因IMFAクリア」の順が大事です。ここを細かく見ておきましょう。
IMFAをクリアしてからをCCRのIクリアの手順が正解です。

★シングルタイマ割り込みの場合
(1)タイマ割り込みの場合,コンペアマッチでIMFAが0→1になります。この値で割り込み要求が発生します。
(2)通常はCCRのIはクリアされているので割り込みを受け付けます。
(3)割り込みを受け付けると,PCとCCRをスタックに退避し,CCRのIをセットし(割り込み禁止),割り込みベクタテーブルを参照して割り込みルーチンに飛んでゆきます。
ここでスタックに退避されたCCRのIはクリア状態です。
(4)割り込みルーチンの処理をします。最初に必要なレジスタの退避をすると思います。IMFAを0にするまでは,割り込み要求は出っ放しになっていますが,CCRのIがセット状態(割り込み禁止)なので,再び割り込み処理がはじまることはありません。
(5)割り込みルーチンのどこかで(経験上は即座にIMFAをクリア)IMFAをクリアします。この時以降,割り込み要求は存在しないことになります。
(6)割り込みルーチンから戻るところで,退避したレジスタの復帰を行ないます。また退避したCCRの復帰も行なうため,CCRのIはクリア状態に戻り,次の割り込みを受け付ける準備が出来ます。

★★シングルタイマ割り込みの場合で割り込みルーチン中でCCRのIをクリアした時(その1)
(CCRのIをクリアしてからIMFAをクリアの手順)
これは手順が逆です。
(1)タイマ割り込みの場合,コンペアマッチでIMFAが0→1になります。この値で割り込み要求が発生します。
(2)通常はCCRのIはクリアされているので割り込みを受け付けます
(3)割り込みを受け付けると,PCとCCRをスタックに退避し,CCRのIをセットし(割り込み禁止),割り込みベクタテーブルを参照して割り込みルーチンに飛んでゆきます。
ここでスタックに退避されたCCRのIはクリア状態です。
(4)割り込みルーチンの処理をします。最初に必要なレジスタの退避をすると思います。IMFAを0にするまでは,割り込み要求は出っ放しになっていますが,CCRのIがセット状態(割り込み禁止)なので,再び割り込み処理がはじまることはありません。
(5)ここでCCRのIをクリアすると,その次の瞬間,IMFA=1の割り込み(自分自身の割り込み)要求を受け付けます。そして(3)と同じ動作になります。
その後,(3)(4)(5)を繰り返して,無限割り込み受け付けになり,H8は暴走状態になります。割り込みルーチン中のIMFAをクリアするところまでたどりつけません。

★★★シングルタイマ割り込みの場合で割り込みルーチン中でCCRのIをクリアした時(その2)
(IMFAをクリアしてからCCRのIをクリアの手順)
この手順が正解です。
(1)タイマ割り込みの場合,コンペアマッチでIMFAが0→1になります。この値で割り込み要求が発生します。
(2)通常はCCRのIはクリアされているので割り込みを受け付けます
(3)割り込みを受け付けると,PCとCCRをスタックに退避し,CCRのIをセットし(割り込み禁止),割り込みベクタテーブルを参照して割り込みルーチンに飛んでゆきます。
ここでスタックに退避されたCCRのIはクリア状態です。
(4)割り込みルーチンの処理をします。最初に必要なレジスタの退避をすると思います。IMFAを0にするまでは,割り込み要求は出っ放しになっていますが,CCRのIがセット状態(割り込み禁止)なので,再び割り込み処理がはじまることはありません。
(5)IMFAをクリアします。この時以降,割り込み要求は存在しないことになります。
(6)ここでCCRのIをクリアすると,新たな割り込みを受け付けることができるようになります。しかし,すでにIMFA=0なのでCCRのIのクリア直後に自分自身の割り込み要求を受け付けることはありません。
(7)割り込みルーチンから戻るところで,退避したレジスタの復帰を行ないます。また退避したCCRの復帰が行なわれますが行なわれても,CCRのIはクリア状態になっています。
すなわち(6)以降,次の割り込みを受け付ける準備が出来ていることになります。

3.多重タイマ割り込みのプログラム

2つのタイマ割り込みを同時に行ない,2つのLEDをそれぞれ固有の周期で動作させます。
次の動作を行なうプログラムです。
1つはITUch0を用いて100μsの割り込みを行います。もう1つはITUch1を用いて5000μsの割り込みを行います。
そしてITUch0の割り込みルーチンは819200μs周期, ITUch1の割り込みルーチンは1280000μs周期でLEDを点滅させます。
ITUch0の割り込み周期が短いので,ITUch1の割り込み中にも,ITUch0の割り込みを受け付ける

多重タイマ割り込みのプログラム

/**********************************************************
多重タイマ割り込みによって2つのLEDのON-OFFを行う
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"

#pragma asm
    .SECTION    MYVEC, DATA, LOCATE=H'000060
    .ORG    H'000060  ;IMIA0
    .DATA.L _timer0INT
    .ORG H'000070  ;IMIA1
    .DATA.L _timer1INT
    .SECTION    P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm

void initTimer0Int(unsigned short int period)
{
    unsigned int period25=(unsigned int)((25*(long int)period+4)>>3);
    ITU0.TCR.BIT.CCLR=1;  /*GRAのコンペアマッチ/インプットキャプチャでTCNTをクリア*/
    ITU0.TCR.BIT.CKEG=0;  /*立ち上がりエッジでカウント*/
    ITU0.TCR.BIT.TPSC=3;  /*内部クロックφ/8でカウント*/
    ITU0.GRA=period25-1;  /*割り込みの周期をperiod[μs]に指定*/
    ITU0.TIER.BIT.IMIEA=1;  /*TCNT=GRAとなったときの割り込み要求を許可*/
    ITU0.TIER.BIT.OVIE=0;  /*オーバーフロー,アンダーフローが発生したときの割り込みを禁止*/
    ITU0.TIER.BIT.IMIEB=0;  /*TCNT=GRBとなったときの割り込みを禁止*/
}

#define startTimer0() (ITU.TSTR.BYTE |= 0x01) /* Timer CH0 スタート */

main()
{
    initLed();
    initTimer0Int(100); /*時間割り込み100μsec ch0使用*/
    initTimer1Int(5000); /*時間割り込み5000μsec ch1使用*/
    INTC.IPRA.BIT._ITU0=1; /*ITU0をプライオリティ1(優先)*/
    E_INT();            /*CPU割り込み許可*/
    startTimer0();      /*時間割り込みタイマスタートch0*/
    startTimer1();      /*時間割り込みタイマスタートch1*/
    while(1);           /*なにもしないループ*/
}

#pragma interrupt(timer0INT)/*この名前の関数は割り込みルーチン仕様である*/
                            /*プログラム中から呼び出してはならない*/
void timer0INT() /*割り込みルーチンの名前はIntTbl.srcで定義されている*/
{
    static int tick=0;
    ITU0.TSR.BIT.IMFA=0; /*Clear IMFA*/
    if ((tick&0x1000)==0x1000) {
        turnOnLed(0);
    } else {
        turnOffLed(0);
    }
    tick++;
}


/*   ●重要●
* Clear IMFAを起動後E_INT()の前にやっておかないといけない。
* 割り込みはIMFAの立ち上がりエッジで起こっているわけでなく,highレベルで
* 起こる。
* もしClear IMFAをこの関数の最後に書いておくと,E_INT()を実行した直後に
* 自分自身の割り込みがかかってしまう。
*/
#pragma interrupt(timer1INT)/*この名前の関数は割り込みルーチン仕様である*/
                            /*プログラム中から呼び出してはならない*/
void timer1INT() /*割り込みルーチンの名前はIntTbl.srcで定義されている*/
{
    static int tick=0;
    ITU1.TSR.BIT.IMFA=0; /*Clear IMFA*/
    E_INT();
    if ((tick&0x80)==0x80) {
        turnOnLed(1);
    } else {
        turnOffLed(1);
    }
    tick++;
}