SH2/7045におけるタイマ割り込み
01Feb2004
12Nov2002
Copyright(C) TNCT CS Kosaka
【1】 はじめに
この文書ではタイマ割り込みに関して説明している。
【2】一般的なコンペアマッチタイマ割り込みの仕組み
SH2CPU内部には「タイマユニット」があり,その中にはシステムクロックをカウントアップするカウンタと,設定された値を保持しているレジスタがあり,この2つの値が一致することによって割り込み要求信号を発生している。割り込み要求信号が発生すると同時にカウンタはリセットされるため,割り込み要求信号は周期的に発生することになる。(発生した割り込み要求信号は割り込み処理手続きの中で消さなければならない。)タイマユニットがこのような動作をするためには事前にこのような動作をするように設定が必要である。
CPUが割り込み許可状態で,割り込み要求信号を受け取ると,タイマ割り込み処理が始まる。
タイマ割り込みが生ずると,メモリアドレスの先頭付近に置かれた割り込みベクタテーブル(割り込みが起こったらどのアドレスにジャンプするかのアドレス表)中から,タイマ割り込みの時に参照するベクタ(具体的には割り込み関数の先頭アドレス)を取り出し,そのアドレスにジャンプする。(正確には,CCR[コンディションコードレジスタ]と現在のPC[プログラムカウンタ]を退避させてから,割り込み関数呼び出しする)
割り込み関数は,次の作業を行なわなければならない。
(1)はじめに各レジスタの値をスタックに退避する
(2)タイマユニットの割り込みフラッグをクリアする(割り込み作業が開始したことを知らせ,割り込み要求を取り下げさせる)
(3)必要な作業を行なう
(4)各レジスタにスタックから退避させた値を復帰する
(5)割り込みリターンする(通常の関数では関数リターンだが,SR[システムレジスタ]の復帰も行なう)
次のプログラムはタイマ割り込みを用いたLEDのON-OFFを行うものである。
割り込み要因はCMT1を用いている。
通常の電源ONの起動時には0番地に書いてあるアドレスから始まる。すなわち(A)_startから処理が始まる。そのあと変数や必要な設定レジスタを設定し,処理は(B)の無限ループとなる。
割り込みは0.5secごとに起動し,次のように動作する。
(1)0x0250番地に割り込み関数のアドレス_int_cmi1が書いてある。割り込みが生じたら_int_cmi1へ処理が移る。
これはリンカスクリプトにかかれている。
この時次の4つのことが自動的に行なわれている。
a)SR[システムレジスタ]をスタック領域にPUSHする
b)SR[システムレジスタ]の割り込みマスクが変更され,割り込み禁止になる
c)現在のPC(プログラムカウンタ)の値をスタック領域にPUSHする。あとで関数から戻るときに使う。
d)PCに_int_cmi1のアドレスを書き込む
(2)PUSH 使用レジスタの値をスタックに退避する
(3)タイマユニットの割り込みフラッグをクリアする(割り込み作業が終了したことを知らせ,割り込み要求を取り下げさせる)
(4)必要な作業(LEDのON-OFFなど)を行なう
(5)POP スタックに退避させた値をレジスタに復帰する
(6)RTE 割り込みリターンする(通常の関数では関数リターンRTSが使われるここではRTE)
この時次のことが行なわれる。
a)(1)c)で退避したもとのアドレスをスタックからPCに復帰する。割り込み前の作業が継続される
b)(1)a)で退避したSR[システムレジスタ]の復帰。割り込みが許可された状態に戻る
romsh7045.xの一部 |
LONG(DEFINED(_int_cmi0)?ABSOLUTE(_int_cmi0):ABSOLUTE(_start)) |
inttest.c |
/********************************************************** main() /****************************************************** |
ここまでの説明をまとめると次のようになる
(1)タイマ割り込みの準備
CPU |
タイマユニット | |||
|
||||
|
→ | 了解 | ||
|
→ | 了解 |
(2)タイマ割り込みの動作
CPU |
タイマユニット | |||
了解 |
← |
時間ですよ | ||
割り込みの受け付け(次の動作を自動的に行なう) a)SR[システムレジスタ]をスタック領域にPUSHする *1 |
時間ですよ | |||
割り込み関数の開始
|
時間ですよ | |||
|
時間ですよ | |||
|
→ | 了解。黙ります。 | ||
|
||||
|
*3 タイマユニットに対して割り込み要求を取り下げさせる。この作業を忘れると割り込み関数が終了直後に,再び割り込みを受け付けて,割り込み関数が起動してしまう。
【3】 タイマ割り込みの仕掛け
ここで解説するタイマ割り込みの記述方法は汎用性を持つものであるが,「GCC」の記述規則に依存するものである。
タイマ割り込み関数の名前を 「_int_cmi1」 とする。(この名前はリンカスクリプトで決まっている)
(1)割り込みベクタ 0X250 に 「_int_cmi1」 の先頭アドレスを書いておく。(割り込みベクタは4バイトである。)
この記述はリンカスクリプトに書いてある。
リンカスクリプト LONG(DEFINED(_int_imia1)?ABSOLUTE(_int_imia1):ABSOLUTE(_start))
(2)割り込み関数であることを示すために「#pragma interrupt」宣言を行なうと,コンパイラが自動的に次の処理を付け加えてくれる。
前処理
1)各レジスタの値をスタックに退避する
後処理
6)各レジスタにスタックから退避させた値を復帰する
7)割り込みリターンする
(3)残されたことは,割り込み関数 「_int_imia1」 には次のことを記述すればよい。
1)実行したい内容
2)タイマユニットの割り込みフラッグをクリア割り込み関数の例
#pragma interrupt /*次の名前の関数は割り込みルーチン仕様である*/
/*プログラム中から呼び出してはならない*/
void int_cmi1()
{
static int tick=0;
if (tick==1) {
turnOnLed(0);
turnOffLed(1);
} else {
turnOffLed(0);
turnOnLed(1);
}
tick=1-tick;
clearCMFlag1(); /* コンペアマッチフラッグのクリア */
}
時間の経過 | 割り込みベクタ | 割り込み関数 int_cmi1 |
0x250にint_cmi1 | ||
↓ | レジスタ退避(この部分はコンパイラが作ってくれる) | |
↓ | タイマ割り込みで実行したい内容 割り込み要因解除 | |
↓ | レジスタ復帰(この部分はコンパイラが作ってくれる) 割り込みルーチンリターン(この部分はコンパイラが作ってくれる) |
【4】 タイマ割り込みの起動
プログラムの起動時にsh_7045.h中にある「タイマ割り込み初期化関数」と「CPUの割り込みマスク設定関数」を呼び,「タイマ起動関数」を呼ぶと,「タイマ割り込み初期化関数」で設定した時間間隔でタイマ割り込みが起こり,「割り込み関数int_cmi1()
」が周期的に起動するようになる。
タイマ割り込み初期化関数で設定する割り込みレベル15にしたら,CPUの割り込みマスク値が15より小さければ,この割り込みを受け付けるようになる。
タイマ割り込み初期化関数 |
void initCMTInt(unsigned long int period,int ch,int intLebel) |
意味 |
ITU1による割り込みタイマーの設定 /* コンペアマッチタイマによるタイマ割り込みの初期化 */ /* 割り込み間隔は引数peiodで単位はμsecである */ /* 0<period<1170104 なので最長1.170104secまで可能 */ /* チャンネルが2つあるのではchは0,1のどちらかを選ぶ */ /* 割り込みレベルintLebelは1〜15にする */ |
タイマ起動関数 |
startCMT1() |
意味 |
CMT CH1 スタート |
【5】 「#pragma
interrupt」の役割の検証
「#pragma
interrupt」の役割をテストプログラムで見てみよう。
このCソースプログラムで関数「testfunc1()」と関数「testfunc2()」は作業は同じだが,関数「testfunc1()」は割り込み関数であると宣言されている。
Cソースプログラム |
int a=0; |
コンパイラが生成したasmソースでみると,関数「testfunc1()」では,関数の初めに利用しているレジスタの退避コードがあり,関数の終わりにレジスタの復帰コードが生成されている。さらに関数の終わりは,関数「testfunc1()」では,「RTE」(ReTurn
from Exception)で終了し,関数「testfunc2()」では,「RTS」(ReTurn from
Subroutine)で終了しているのが分る。
r14のpushとpopはGCCの都合によるもの。
コンパイラが生成したasmソース | |
.global _testfunc1 |
|