【PIC18シリーズの割込み】
PIC18シリーズになって割込みが強化され、高位と低位2つのレベルの優先順位のある
割込みとなりました。そして、高位の割込みは、低位の割込みを処理中でも優先的に受け
付けられて割込むことが出来ます。
この優先順位分けを使わないで、従来と同じ割込みとすることも出来ます。
このようなレベル分けした構成と、従来と同じ1レベルだけによる構成と両方出来るように
するため、各割込み要因ごとに設定レジスタが追加され、下記のような4つのレジスタと
なりました。
(1)全体レベル設定レジスタ(RCON、INTCON)
優先順位分けをする/しないの設定を全体で指定できます。RCONレジスタのIPENビット
を「1」にすると優先順位分けをするになり、「0」にするとしないで、従来と同じ1レベルと
して扱います。デフォルトは0となっています。
優先順位を分けた時には、全体の割り込みを許可/禁止するビットGIEも2つに分離さ
れて、GIEHとGIELとなります。
(2) 割込みフラグ(INTCON、INTCON3、PIR1、PIR2)
従来と同じ割込みを受け付けたことを示すフラグで、受付後にソフトウェアでリセットする
必要があります。
(3) 割込みイネーブル(INTCON、PIE1、PIE2)
各割込み要因ごとに割り込みを使うことを設定するためのものです。
(4) 割込みレベル設定(INTCON3、IPR1、IPR2)
新たに追加された設定情報で、各要因ごとに優先順位をHighにするかLowにするかを
指定します。
【MPLAB-C18での割込みの扱い】
MPLAB-C18では、割込み処理の定義は#pragma擬似関数で指定します。 しかし、この指定
は、単に、戻り関数 RETFIEをNormalにするか Firstにするかを決めているだけです。
#pragmaの書式は下記のような2種類となっていて優先順位の宣言をすることになります。
(1) #pragma interrupt functionname [sectionname] save = symmbollist
(2) #pragma interruptlow functionname [sectionname] save = symbollist
ここでinterruptは高位割込み処理関数であることを定義し、interruptlowは低位割込み処理
関数であることを示します。
高位割込み処理では、レジスタの退避/復旧にshadow registerを自動的に選択し、WREG、
BSR、STATUSの各レジスタの退避復旧はは自動的に行われます。また、さらに追加レジスタを
退避したいときには、save= で指定すれば追加されます。
この高位レベルでは上記レジスタの退避には、自動的にshadow registerを使うので、この割込
み処理関数の最後は、Fast RETFIEとする必要がありますがこれを自動的に生成してくれます。
これに対し、interruptlowの方は、レジスタの退避は自動的には指定されないので、必ず
save=で指定する必要があります。また処理の最後にはNormalのRETFIEで戻るように自動生成
されます。このRETFIEの前でsaveで指定したレジスタの復旧処理も自動生成されます。
いずれの場合にも、MPLAB-C18では割り込みベクタへの自動配置はしてくれませんので、
割り込みベクタの番地には、このそれぞれの割込み処理へのジャンプ命令関数を追加する必要
があります。
【単純割込みのプログラミング】
優先レベルを使わず、割込み要因も1種類だけの時には、下記のようにします。
(1) 割込みの宣言をします。
ここでは#pragmaを使って下記フォーマットで宣言します。
#pragma interrupt fname save=symbol list
fname :割込み処理関数名
symbol list :退避復旧するレジスタの名称をカンマで区切って並べます。
このときWREG、BSR、STATUSは自動的に退避復旧されます。
(2) 割込みベクタにジャンプ命令をセットする。
ベクタは0x08番地のみとなりますのでここに下記関数でジャンプ命令をセットします。
#pragma code isrcode = 0x08 //isrcodeの名称は何でもOK
void isr_direct(void) //isr_directの名称も何でもOK
{ _asm goto fname _endasm} //fnameは割込み処理関数名
(3) 割込み処理関数を用意します。
上記定義と同じ名前の関数を用意します。この関数の展開後の最後に自動挿入される
RETFIE命令はFirst Return となり、WREGとBSR、STATUSレジスタは自動復旧されます。
関数の直前に #pragma code を置いてメモリエリアを戻しておきます。
(4) 割込みを許可します。
全体割込みGIEと、必要な場合には周辺割込みPEIEを許可します。
INTCONbits.PEIE=1;
INTCONbits.GIE=1;
《実例》
下記プログラムはタイマ0を割込みで使ってLEDを点滅させるプログラム例です。
★ 割込みテストプログラム(その1)
【同一レベル複数要因のプログラミング】
優先レベルを使わず、複数の周辺モジュールの割込みを同時に扱うためには、下記のように
します。
(1) 上記と同じ
(2) 上記と同じ
(3) 割込み処理関数の中で、複数の割込み要因を区別して両方の処理を実行します。
このとき割込み要因の区別は、各割込みフラグが1かどうかをテストすることで行います。
つまりフラグが1になっている周辺機器の割込みが入ったことになります。
この場合、同時に複数の要因が入っている可能性があるので、ひとつの割込み処理を
完了しても次のフラグを確認し、全部のフラグを見るようにします。
またフラグ確認後はそのフラグをクリアするのを忘れないようにします。これを忘れると
永久に割り込みが入りっぱなしとなってしまいます。
(4) 割込みを許可します。
全体割込み許可GIEと必要であれば周辺モジュールの割込み許可PEIEの両方を
許可する必要があります。
《実例》
下記プログラムはタイマ0とタイマ1を同じレベルの割込みを使って動作させた例です。
★ 割込みテストプログラム(その2)
【優先レベルを使ったプログラミング】
優先レベルを使った割り込みで複数の割込み要因を同時に動かす時は、下記のように
します。
(1) 優先レベルを使う宣言をする。
この宣言は、下記のようにRCONレジスタのIPENビットをセットすることで行います。
RCONbits.IPEN=1;
(2) 各周辺モジュールごとに優先順位をどちらにするかを定義します。この定義はデフォルト
は高位レベルになっているので、低位側だけを宣言すれば大丈夫で下記のようにします。
つまり各周辺モジュール毎に用意された優先レベル指定ビットをクリアします。
IPR1bits.TMR1IP=0;
(3) 割込みの宣言をします。
ここでは#pragmaを使って両方のレベルを宣言します。
#pragma interrupt fname1 save=symbol list
#pragma interruptlow fname2 save=symbol list
fname1 :高位側の割込み処理関数名
fname2 :低位側の割込み処理関数名
symbol list :退避復旧するレジスタの名称をカンマで区切って並べます。
高位側のWREG、BSR、STATUSは自動退避復旧されます。
(4) 両方の割込みベクタにジャンプ命令をセットする。
ベクタは0x08番地と0x18番地となりますのでここに下記関数でジャンプ命令をセットします。
#pragma code isrcode = 0x08
void isr_direct(void)
{ _asm goto fname1 _endasm } //fname1は優先割込み処理関数名
#pragma code lowcode = 0x18
void low_direct(void)
{ _asm goto fname2 _endasm } //fname2は低位レベル処理関数名
(5) 割込み処理関数を用意します。
上記定義と同じ名前の関数を用意します。高位レベルの関数の展開後の最後に自動挿入
されるRETFIE命令はFirst Return となり、WREGとBSR、STATUSレジスタは自動復旧され
ます。
低位レベルの方はNormal Returnとなりますが、(3)項のsave=で指定したレジスタの復旧
プログラム部分は自動生成されます。
最初の関数の直前に #pragma code を置いてメモリエリアを戻しておきます。
(6) 割込みを許可します。
この場合には優先順位の両方の割込みを許可する必要があります。下記のようにします。
INTCONbits.GIEH=1; // 高レベル許可
INTCONbits.GIEL=1; // 低レベル許可
《実例》
下記プログラム例は、タイマ0とタイマ1をそれぞれ別の優先順位の割込みで扱い
発光ダイオードを点滅させるプログラムです。
★ 割込みテストプログラム(その3)