マイクロコンピュータH8/3664におけるタイマ割り込み

12Nov2002
Copyright(C) coskx

1.はじめに
小坂の開発環境では,タイマ割り込みに関してからくりが仕込んである。この文書はこのからくりについて説明している。

2.ユーザから見た小坂の開発環境のタイマ割り込み
ユー ザはh8_3664.h中にある「タイマ割り込み初期化関数(この関数はタイマユニットを初期化する。タイマユニットは引数で指示された感間隔でCPUに 割り込みをかけるようになる)」と「CPUの割り込み許可関数E_INT()(この関数はCPUが割り込みを受け付けるように設定する)」を呼び,「タイ マ起動関数」を呼ぶと,「タイマ割り込み初期化関数」で設定した時間間隔でタイマ割り込みが起こり,「割り込み関数」が周期的に起動するようになる。

なお「タイマ割り込み初期化関数」と「タイマ起動関数」「タイマ停止関数」は次のようになっている。
void initTimerAInt(short int n)
  TimerAを利用したタイマー割り込み初期化
 引数の値とタイマー割り込み周波数
  7    7812.5[Hz]
  6    1953.125[Hz]
  5     488.28125[Hz]
  4     244.140625[Hz]
  3     122.0703125[Hz]
  2      30.51757813[Hz]
  1      15.25878906[Hz]
  0       7.629394531[Hz]

void startTimerAInt(void)
  TimerAを利用したタイマー割り込みスタート
void stopTimerAInt(void)
 TimerAを利用したタイマー割り込みストップ

3.タイマ割り込みの仕組み
  H8CPU内部には「タイマユニット」があり,その中にはシステムクロックをカウントアップするカウンタと,設定された値を保持しているレジスタがあり, この2つの値が一致することによって割り込み要求信号を発生している。割り込み要求信号が発生すると同時にカウンタはリセットされるため,割り込み要求信 号は周期的に発生することになる。(発生した割り込み要求信号は割り込み処理手続きの中で消さなければならない。)タイマユニットがこのような動作をする ためには事前にこのような動作をするように設定が必要である。
 CPUが割り込み許可状態で,割り込み要求信号を受け取ると,タイマ割り込み処理が始まる。
  タイマ割り込みが生ずると,メモリアドレスの先頭付近に置かれた割り込みベクタテーブル(割り込みが起こったらどのアドレスにジャンプするかのアドレス 表)中から,タイマ割り込みの時に参照するベクタ(具体的には割り込み関数の先頭アドレス)を取り出し,そのアドレスにジャンプする。(正確に は,CCR[コンディションコードレジスタ]と現在のPC[プログラムカウンタ]を退避させてから,割り込み関数呼び出しする)

割り込み関数は,次の作業を行なわなければならない。

(1)はじめに各レジスタの値をスタックに退避する
(2)タイマユニットの割り込みフラッグをクリアする(割り込み作業が開始したことを知らせ,割り込み要求を取り下げさせる)
(3)必要な作業を行なう
(4)各レジスタにスタックから退避させた値を復帰する
(5)割り込みリターンする(通常の関数では関数リターンだが,CCR[コンディションコードレジスタ]の復帰も行なう)

ここで#pragma interrupt命令を使うとCで書いた関数を割り込み関数にすることができる。
割り込み関数は通常の関数と違い,コンパイラが次のような内容の機械語コードを作ってくれる。

   1)使用するレジスタの値をスタックに退避する
   2)割り込み関数内でユーザが行いたい作業をする
   3)タイマユニットの割り込みフラッグをクリアする
   4)各レジスタにスタックから退避させた値を復帰する
   5)割り込みリターンする

具体的には次のように書いてある。

(1)割り込みベクタアドレス 0X26 に割り込み関数interrupt_timerA() のアドレスを書いておく。
(割り込みベクタは2バイトである。)
(2)#pragma interruptにより関数interrupt_timerA() が割り込み関数だと宣言した上で割り込み関数を記述する。

3.タイマ割り込みの記述
(1)割り込みベクタアドレスの記述
これはアセンブリファイルに記述する。

    .CPU 300HN
    .IMPORT     _interrupt_timerA
    .SECTION    MYVEC, DATA, LOCATE=H'0026
    .ORG        H'0026
    .DATA.W     _interrupt_timerA      ;タイマーA割り込みベクトル
    .end

(2)割り込み関数を記述する

#define clearTimerAflag() (IRR1.BIT.IRRTA=0)
#pragma interrupt(interrupt_timerA)
/*この名前の関数は割り込みルーチン仕様である*/
/*プログラム中から呼び出してはならない*/
void interrupt_timerA(void)
{
    cnt++;
    if (cnt==30) cnt=0;
    clearTimerAflag();
}

 

3.割り込み関数の記述テスト
「#pragma interrupt」の役割をテストするために次の関数をC言語で記述し,コンパイラがどのようなアセンブリコードを作っているかを検証した。

C言語ソース

volatile int a;

void normalfunc(void)
{
    a++;
}

#pragma interrupt(interruptfunc)
void interruptfunc(void)
{
    a++;
}

コンパイラが作ったアセンブリコード
          .SECTION    P,CODE,ALIGN=2
_normalfunc:
          MOV.W       @_a:16,R0
          INC.W       #1,R0
          MOV.W       R0,@_a:16
          RTS

_interruptfunc:
          PUSH.W      R0
          MOV.W       @_a:16,R0
          INC.W       #1,R0
          MOV.W       R0,@_a:16
          POP.W       R0
          RTE

          .SECTION    B,DATA,ALIGN=2
_a:
          .RES.W      1

割り込み関数の方にはPUSH,POPが挿入されており、使用するレジスタの待避、復帰が行われているのがわかる。