例外ベクタを調べた (5) [ColdFire V2]
RAMにも例外ベクタ・テーブルのような構造があることが判明しました。 このベクタ・テーブルが本当に使えるのか、実験してみます。
割り込みベクタを書き換える実験
もし、 RAM にある例外ベクタを CPU が使用しているのであれば、これらベクタを書き換えるだけで、割り込みを使用できる可能性があります。 また、 ROM にある例外ベクタを CPU が使用している場合でも、 VBR レジスタを変更すれば、 RAM にある例外ベクタ・テーブルを使用させることができるはずです。
実験は、普通に割り込みを受け入れさせる手順となんら変わりません。
どの割り込みを使おうか
割り込みを使うまでには、色々と準備が必要です。 まず、実験で使用する割り込みを決めます。 今回は、比較的処理が簡単なPIT1割り込みを使います。
PITについては、SilentCが使っていないモジュールを探せ(3)で調査だけしましたが、割り込みを発生する以外の仕事をしてくれそうになかったので、使ってみるまでには至りませんでした。 プリスケーラを256(28)分周に設定して、周期レジスタを29297カウントに設定すると、割り込み周期は約250ミリ秒になります。 その他の設定は、PIT0と同じにしておきました。
割り込み処理ルーチンを用意する
LNKLED端子をGPIO設定に変更しておき、PIT1の割り込み処理ルーチンでLNKLEDを反転させるとおなじみのLEDピカピカができるはずです。 そのためには、割り込み処理ルーチンを置く場所が必要です。
割り込み処理ルーチンは、前回調べた、例外ベクタ・テーブルの未使用部分である 0x20000300 からのアドレスに配置しました。 たぶん、この部分が使われることはないでしょう。
2F00 move.l d0,-(a7) ; save D0 3039 4016 0000 move.w PCSR1,d0 0080 0000 0004 ori.l #0x0004,d0 33C0 4016 0000 move.w d0,PCSR1 ; Clear PIF flag 1039 4010 0015 move.b PORTLD,d0 0A80 0000 0002 eori.l #0x02,d0 13C0 4010 0015 move.b d0,PORTLD ; Toggle LNKLED 201F move.l (a7)+,d0 ; restore D0 4E73 rte
ハンド・アセンブルは、何年ぶりだろう。 効率など微塵も考えていないプログラムですが、それにしてもコード効率が悪く見えるな。 中身は、PIT1のフラグをクリアし、LNKLED(PLD[1])を反転させているだけです。
さらに、割り込み処理ルーチンの場所を例外ベクタ・テーブルに書き込む必要があります。 PIT1の例外ベクタは、0x200001E0番地に書き込みます。
これらの一連の処理は、 write_pit_isr() という関数に記述しました。
PIT1割り込みを許可する
PIT1割り込みを許可するためには、二箇所のマスク・ビットを設定する必要があります。 ひとつは、PIT1モジュールのPCSR1[PIE]フラグです。 このフラグをセットすることで、割り込みが許可されます。
もうひとつは、INTC0モジュールのIMRH0[INT_MASK[56]]ビットです。 このビットをクリアすることで、割り込みが許可されます。
本当は、もう一箇所、INTC0モジュールのIMRL0[MASKALL]ビットをクリアする必要があるのですが、SilentCで他のモジュールの割り込みがすでに使用されているため、最初からクリアされています。
PIT1の割り込みレベルを設定する
8ビットマイコンの場合には、このぐらいで割り込みが使えるようになるのですが、MCF52233の場合には、もうひと手間必要です。 それは、PIT1割り込みのレベルを指定することです。
ColdFireの場合、多重割り込みを前提として作られているので、割り込み処理中に別の割り込みが発生する可能性があります。 このときに、新たに発生した割り込みを受け入れるかどうかを判断するのが割り込みレベルという概念です。 簡単に言うと、ある割り込み処理中は、処理中の割り込みよりも割り込みレベルの高い割り込みしか受け入れません。
今回の実験では、LEDピカピカという比較的軽い処理を行わせるので、レベル1割り込みとして処理させることにしました。 レベルを設定するレジスタは、0x40000C78番地のICR056です。 レベルの初期値は0です。 レベル0の割り込みは、CPUと同じレベルなので、このままでは割り込みは発生しません。
このレジスタには、複数の割り込み要求が発生した場合の優先順位を指定することも出来ます。 優先順位も最低の0としておきます。
PIT1割り込みでLEDピカピカ
全プログラムは、こんな風になりました。
char *portld=0x40100015; char *ddrld =0x4010002D; char *pldpar=0x40100075; int *pcsr1 =0x40160000; int *pmr1 =0x40160002; int *pcntr1=0x40160004; long *imrh0 =0x40000C08; char *icr056=0x40000C78; main() { // Prepare GPIO *portld = 0x00; // PORTLD[1:0]=0 *ddrld = 0x03; // DDRLD[1:0]=1 *pldpar = 0x00; // PLDPAR[1:0]=0 // Prepare INT write_pit_isr(); *imrh0 &= 0xFEFFFFFF; // enable PITI *icr056 = 0x08; // LEVEL=1 // Prepare PIT *pmr1 = 29297-1; // Period count *pcsr1 = 0x081F; // Start PIT1 #stop 0 for(;;){ if (Getc(0)=='q')break; PrNum(*pcntr1);PrStr(" ");Sleep(10); } *pcsr1 = 0x0000; // Stop PIT1 } write_pit_isr() { long *v = 0x200001E0; // vector #120 long *p = 0x20000300; // pit_isr *v = p; *p++=0x2F003039;*p++=0x40160000; *p++=0x00800000;*p++=0x000433C0; *p++=0x40160000;*p++=0x10394010; *p++=0x00150A80;*p++=0x00000002; *p++=0x13C04010;*p++=0x0015201F; *p++=0x4E730000; }
メインルーチンでは、'q'キーの入力を待ちながら、PIT1カウンタの値を延々と表示し続けるプログラムが走っています。 安全のため、プログラム終了時には、PIT1を停止させるようにしました。
プログラムを実行した結果、みごとにLNKLEDがピカピカを始めました。 どうやら、RAMに展開された例外ベクタが使用されている様子です。
参考文献
Interface (インターフェース) 2008年 09月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/07/25
- メディア: 雑誌
コメント 0