外部割り込みとは?
外部割り込みとは、どのように使うか
ままでの割り込み(内部割り込み) との違いは?
いつも必ずする処理ならmainのループですればいいわけです。
タイマー割り込みのように、目覚ましの針を仕込んで、セットした時間が来たとき処理する割り込み。
というタイマ-割り込み以外に外部からお知らせが来て実行するのが外部割り込みです。
タイマー割り込みは内部で作り、内部で起こった要因によって処理を実行するのに対し,
外部から発生する要因で起動するのが 外部割り込み.
いつも必ず定期的に実行する処理なら、こんなものを使わなくてもmainのループですればいいわけですが
ときどきやってくるかもしれない処理、
不定期にやってくる、、 もしかしたら1度もやってこないかもしれい、
そんな場合に使えます。
例えば、
ドロボー関知のセンサが反応した! = (外部割り込み発生)
↓
ドロボー対策の処理実行 =(割り込み処理)
ドロボーが来そうなところに、センサを仕込んでおいて
そのセンサ情報を外部割り込み入力にしておくと
普段はmainのループをつつがなく実行している3664Fは
センサが反応すると、突然、外部割り込み処理を実行します。
ドロボー対策の処理を終えると
また、何事もなかったように元の処理の続きを始める。。とこんな感じです。
不定期に、突然に、たまに、やってくるセンサ情報
そんなものなどに、この外部割り込みを適用すればいいでしょう。
外部割り込み
3664Fの外部割り込み機能と
割り込み関数
3664Fには外部割り込み要因として NMI、IRQ0~3、WKPの6つがあります。(簡単に説明)
- NMI割り込み
- NMI端子の入力で発生(立ち上がり、立ち下がり) 最優先の割り込み
割り込み関数 - GDLでは void int_nmi (void) - IRQ割り込み
- IRQ0~IRQ3端子(P14~P17と兼用)の入力で発生(立ち上がり、立ち下がり)
それぞれの端子で違う割り込み関数を実行できる。
割り込み関数 - GDLでは void int_irq0 (void) ~ int_irq3() - WKP割り込み
- WKP0~WKP5端子の入力で発生(立ち上がり、立ち下がり)
端子は6コあるけれど、すべて同じ割り込み関数が起動します。
割り込み関数 - GDLでは void int_wrp (void)---(wrpになっています、割り込みベクタは確認していませんが多分これでしょう)
また、割り込み発生の入力信号は
立ち上がり、立ち下がり のどちらかを選択できます。
IRQ外部割り込み
IRQというとパソコン自作派はどっかで聞いたことのある名前ですね。
今回は、IRQ外部割り込みをためしてみます。
外部から割り込み関数を起動する機能です。
IRQ外部割り込み機能では
IRQ端子に入力が入ると → IRQ割り込み関数が起動
という処理です。
IRQ0~3まで4つの外部割り込みを処理できます。
今回は
IRQ0端子入力 → void int_irq0 (void)割り込み関数起動
をします。
IRQ0 から IRQ3に対し、それぞれ
割り込み関数 void int_irq0 (void) ~ void int_irq3 (void)
の4つが用意されています。
今回は、スイッチが押されたのを要因として検出し、
それで割り込み関数IRQを起動しLEDを点滅させる処理をします。
回路図
外部割り込み信号として、IRQ0端子(P14)にSW入力を入れます。
(普段low、スイッチ押し下げ時high)
通常LOWがH8に入力され、SW押し下げでHIGHになります。このSW押し下げで、割り込み発生です。
LEDはP20に接続しています。
プログラム
FQ_IRQ_INP_blink
#include <3664.h>
void msecwait(int msec)
/*なにもしない時間稼ぎ関数*/
{
int i,j;
for (i=0;i< msec;i++) {
for (j=0;j<1588;j++); /*1588は約1/1000s*/
}
}
void int_irq0 (void){
int k;
for(k=0;k<10;k++){
IO.PDR2.BIT.B0=0;
msecwait(50);
IO.PDR2.BIT.B0=1;
msecwait(50);
}
IRR1.BIT.IRRI0=0; //IRQ0割り込みフラグをクリア
}
int main(){
/*IRQ割り込み設定*/
IEGR1.BIT.IEG0=1; //IRQ0端子入力の立ち上がりを検出
IENR1.BIT.IEN0=1; //IRQ0割り込み要求ON
/*出力LED IOポート設定*/
IO.PMR1.BYTE=0x10; //0001 0000 P14をIRQ0端子に設定
IO.PCR1=0xFF; //全部出力に設定
//PMR1でIOピンに設定されていると、ここで入出力設定する
IO.PCR2=0xFF; /* 全て1にセットし、出力に設定 1111 1111 */
EI;
while (1){
IO.PDR2.BIT.B0 = 1;
}
}
突然の侵入者・・・そんな感じをイメージして
- 普段はmainのループでLEDが点灯してる(通常状態)
↓ - 外部割り込み発生(突然の侵入者で、ボタンが押されたということに)
↓ - 割り込み関数IRQ0で LEDがせわしなく高速点滅して警告に変わる
こんな風にイメージすれば、なんとなく外部割り込みも理解しやすのではと。。。
そんな点滅にしてみました。
レジスタ設定
IRQ外部割り込みにに関しては
1.PMRレジスタ | IRQポートに設定 |
2.IEGR1レジスタ | 割り込みエッジ選択 |
3.IENR1レジスタ | IRQ割り込み許可 |
IRR1レジスタ | 状態レジスタ (プログラム内で状態を見るのに使用) |
のレジスタがあります。
1.IRQポートに設定
まず、外部割り込み入力を受付る入力ポートの設定です。
3664FではIOと兼用ポートとされているP14~ を
IOポートではなく、外部割り込み用のIRQポートに設定します。
その設定はPMRレジスタにします。
このへんは IOでLEDを光らせる も参考に。。
PMR1(ポートモードレジスタ) 7 6 5 4 3 2 1 0 IRQ3 IRQ2 IRQ1 IRQ0 / / TXD TMOW 0 0 0 1 / / 0 0
IRQに関係あるのは4-7ビットまで
IO.PMR1.BYTE=0x10; ( 0001 0000 )
とIRQ0だけを 1にして IRQ端子に設定 他は0でIO端子になる
PCRレジスタは IO.PCR1=0xFF; //全部出力に設定 としてあります。
これは端子機能としては
PMR1レジスタを IRQ設定にしたピンは 入出力設定 PCR1レジスタの設定は入力にしても出力にしても関係なくなり、無視されます。
ここでは、他のピンを出力にしておくので、全部を出力に IO.PCR1=0xFF; //全部出力に設定してあります
2.割り込みエッジ選択
外部の信号での割り込みには HIGHへの立ち上がり と LOWへの立ち下がり の検知方法があります。
(図のような部分が信号の
立ち上がりと立ち下がり)
外部割り込みの IRQ入力信号は
○立ち下がり設定では
ずっとHIGH だったものが
突然
HIGH→LOW になったときが
立ち下がり なので
このとき 割り込みがかかり
割り込み関数が起動します。
立ち下がり後
その後はずっとLow状態でも、
割り込み動作はエッジをとらえた1度だけ。
立ち下がり だけをとらえて反応します。
○立ち上がり設定では
反対に
ずっとLOW だったものが 突然
LOW→HIGH になったときが
立ち上がり なので
このとき 割り込みがかかり
割り込み関数が起動します。
そのエッジの選択を IEGR1レジスタで設定
IEGR1レジスタ
は IRQ0~3 までと NMI の設定
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
NMIEG | / | / | / | IEG3 | IEG2 | IEG1 | IEG0 |
NMI端子 | IRQ3端子 | IRQ2端子 | IRQ1端子 | IRQ0端子 | |||
0 | / | / | / | 0 | 0 | 0 | 1 |
IRQに関係あるのは0-3ビットまで。
今回は P14に当たるIRQ0を1にします。
何も指定しないと初期値0なので、 ビットアクセスで
IEGR1.BIT.IEG0=1; //IRQ0端子入力の立ち上がりを検出
とだけしています。
3.IRQ割り込み許可
IRQ0~3の割り込み許可(イネーブル)設定はIENR1レジスタで設定
IENR1レジスタ
このビットを1にするとIRQ端子の割り込み要求がイネーブルになる
IENR1 7 6 5 4 3 2 1 0 IENDT IENTA IENWP / IEN3 IEN2 IEN1 IEN0 直接遷移割り込み タイマA WKP0-5共通 IRQ3端子 IRQ2端子 IRQ1端子 IRQ0端子 0 0 0 0 0 0 0 1
IRQに関係あるのは0-3ビットまで。
何も指定しないと初期値0なので、 ビットアクセスで
IENR1.BIT.IEN0=1; //IRQ0割り込み要求ON
と目的ビットだけ1にしています。
また、その他、LEDの端子で
IO.PCR2=0xFF; /* 全て1にセットし、出力に設定 1111 1111 */
IO端子を出力設定にしておきます。
また、これまでに出てきた時間稼ぎループで間隔を決めています。
実行
割り込み入力エッジに
立ち上がりを設定しているので、
ずっとIRQ0端子の入力がLOWだったものが、SWを押し突然HIGHになることで
割り込みが発生し、割り込み関数irq0()が起動し、LEDが高速点滅する
という処理です。
変更し、立ち下がり に設定してみる
IEGR1.BIT.IEG0=1; //IRQ0端子入力の立ち上がりを検出
のレジスタ設定部分を
=0 として
立ち下がり検出に変えてみましょう。
すると、SWを押したときは、なにも変わりませんが、
離したとき割り込みがかかる設定になります。立ち下がりを検出しています。
(この実験ではSWにチャタリング対策が必要になりそうです)
プログラム2
IRQ割り込み2コ
割り込みをもう一つ増やし2コにしてみました。外部割り込みIRQ2コの実験
単にSWをもう一つ増やしIRQ1に入れ、その対象のirq1()割り込み関数を少し変え点滅を遅くする割り込み関数IRQ1を付け加えたただけです。
ファイル名
/* IRQ外部割り込みのテスト
IOボタン入力で
IRQ0割り込み関数を起動してみる
:IRQ端子は IRQ0-P14
|
IRQ3-P17 と兼用
p14(IRQ0端子)にsw入力
p15(IRQ1端子)にsw入力
p20にLED出力
動作: 割り込み0,1で点滅パターンが変わる
*/
#include <3664.h>
void msecwait(int msec)
/*なにもしない時間稼ぎ関数*/
{
int i,j;
for (i=0;i< msec;i++) {
for (j=0;j<1588;j++); /*1588は約1/1000s*/
}
}
void int_irq0 (void){
int k;
for(k=0;k<6;k++){
IO.PDR2.BIT.B0=0;
msecwait(200);
IO.PDR2.BIT.B0=1;
msecwait(200);
}
IRR1.BIT.IRRI0=0; //IRQ割り込みフラグをクリア
}
void int_irq1 (void){
int k;
for(k=0;k<12;k++){
IO.PDR2.BIT.B0=0;
msecwait(50);
IO.PDR2.BIT.B0=1;
msecwait(50);
}
IRR1.BIT.IRRI1=0; //IRQ割り込みフラグをクリア
}
int main(){
/*IRQ割り込み設定*/
IEGR1.BIT.IEG0=1; //IRQ0端子入力の立ち上がりを検出
IEGR1.BIT.IEG1=1; //IRQ1端子入力の立ち上がりを検出
IENR1.BIT.IEN0=1; //IRQ0割り込み要求ON
IENR1.BIT.IEN1=1; //IRQ1割り込み要求ON
/*出力LED IOポート設定*/
IO.PMR1.BYTE=0x30; //0011 0000 P14を IRQ0端子に設定
IO.PCR1=0xFF; //全部出力に設定
//PMR1でIOピンに設定されていると、ここで入出力設定する
IO.PCR2=0xFF; /* 全て1にセットし、出力に設定 1111 1111 */
EI;
while (1){
IO.PDR2.BIT.B0 = 0;
}
}
この外部割り込み処理 IRQ0,1 2つはは、終了までに(点滅時間を全部終える)まで時間がかかります。
IRQ0割り込みは終了までにだいぶ時間がかかります。
そのIRQ0の割り込み実行中に割り込み許可して、外部割り込みIRQ1に入力をいれて割り込み中にさらなる外部割り込みはかかりません。
H8 Tinyでは NMI(最優先)の割り込み 以外は多重割り込みはかからない。(C言語でH8マイコンをを使いこなす)より
参考資料
とても説明がわかりやすいマイコンサイトです
Flight Fether H8講座
- twtter
- google+
- hatena