今回は、PICマイコンの割込み処理の仕組みと必要な設定内容について説明します。
PICマイコンの割込みの仕組み
「割り込み処理」では、スイッチ状態の変化などをきっかけに、メイン処理を一時中断して割り込み処理を行う、という動作です。
このように聞くと、プログラムでは「スイッチ状態の変化を検知」の設定と「割り込み処理」の内容を書くだけのような印象ですよね。
最初からこのようなことは申し上げづらいのですが、、、PICマイコンの割り込み処理設定はちょっと複雑です。(PICマイコンに限らず、他社のマイコンでも似たような設定があります)
そこで、今回の記事では割込み処理のプログラムを作成する前に、PICマイコンの割込み処理の内部の様子を理解しておきたいと思います。
前回の記事で、割込みにはいろいろな種類があることを説明しました。
例えば「ピンの入力が変化したとき」や「外部とデータ通信するとき、外部からデータが入ってきたとき」などです。
このように、割り込みは何種類もあるので、割り込み処理を行う場合、プログラムでは次の対応が必要になります。
- どの種類の割込みを有効にするか、という「割込みの種類の設定」
- 割込みが発生した場合にどのような処理をするか、という「割込み処理プログラム」
これから、それぞれについて詳しく説明します。
❶ 割込みの種類の設定
設定内容
PICマイコンはデフォルトではすべての種類の割込みは無効になっています。
割込み処理を行う場合、必要な割込みを有効化する必要がありますが、有効化する設定はちょっと複雑です。
厳密ではありませんが、PICマイコン内部の割込みの有効化設定は以下のようになっています。

例えば、ピンの入力値に変化があった場合に割込み処理を行いたい場合は、まずは「入力値の変化があったら割込みを有効にする」という設定を行います。
ただ、これだけではピンの入力値変化があった場合でも割込みは発生しません。さらに、そもそも割込みを発生させるかどうか、という元栓となるような有効化設定がありますので、この設定を行います。
ここまでのことをまとめると、割込みを有効化するには、次の2つの設定が必要になります。
- 割込み種類別の有効化の設定を行う
- 割込み自体の有効化の設定を行う
さらに、ピンの入力値変化があった場合に割込みを行う場合、追加の設定が必要です。
ピンの入力値変化といっても、「0→1」の変化と「1→0」の変化があります。
どちらの変化が起こったときに割込みを発生させるか、という設定が必要になります。
ここまでをまとめると、ピンの入力値変化があった場合に割込み処理を行いたい場合、全部で次の3つの設定が必要になります。
- ピンの入力値変化の種類の設定を行う
- 割込み種類別の有効化の設定を行う
- 割込み自体の有効化の設定を行う
それでは、これら3つの設定をどのレジスタで行うか見ていきましょう!
設定プログラム
今回は、RA3ピンの値が「1 → 0」に変化したときに割込みを発生する、というケースの設定方法を確認します。
図の中にあるIOCAN3
、IOCIE
、GIE
レジスタは、1にすると有効、0にすると無効になります。
まず、RA3ピンが1→0に変化したときに割り込み発生を有効化する場合は以下のプログラムになります。
IOCAN3 = 1; // RA3ピンが0->1変化のときに割り込み発生する
IOCIE = 1; // ピンの入力変化があった場合に割り込みを発生する
GIE = 1; // そもそも割り込み自体を発生する
無効化する場合はいくつか方法があります。
例えば、割り込み処理として、RA3ピンの入力値変化のみを扱っている場合に割り込み処理を無効化する場合、元栓となるGIEレジスタで制御しても問題ありません。
その場合は、割り込みを無効化したいタイミングで次のように書きます。
GIE = 0; // 割り込み全体を無効化してしまう
また、例えば割り込み発生信号として「RA3ピンの入力変化」と「データ受信」の2種類を処理しているとき、「RA3ピンの入力変化」だけ無効化したい場合は、次のように書けばOKです。
IOCIE = 0; // ピンの入力変化の割り込みを全て無効化する。IOCAN3 = 0;でもOK
設定レジスタまとめ
呪文のようなレジスタ名が3つも出てきて、どれがどれだかわからなくなってきますよね。
そこで、それぞれのレジスタ名の略も確認しておきたいと思います。
- GIE
Global Interrput Enableの略で、英語交じりの日本語では「グローバルな割り込み処理許可」です。
もう少し噛み砕くと「全体の割り込み処理許可」という意味で、割り込み処理自体を有効にするか無効にするかの元栓の設定になります。 - IOCIE
Interrupt-On-Change Interrupt Enableの略で、「ピンの状態変化による割り込み許可」という意味になります。
ピンの状態が変化した時の割り込みを有効にするか無効にするか、ピン変化の元栓の設定になります。 - IOCAN
Interrupt-On-Change port-A Negative edgeの略で、「PortAのピンの状態変化による割り込みのうち、1から0に変化した時の割り込み許可」というような意味になります。
Negativeとは、1から0に変化することで、反対は0から1に変化する場合のPositiveです。
実際にIOCAP
(最後のPはPositiveのP)というレジスタもあります。スイッチを押した時に0から1に変化する場合はIOCAP
のレジスタを使用することになります。
また、特定のピンの割り込み設定をする場合はIOCAN3
などのように、ピン番号をつけます。
割り込みの設定関連は以上になります。
❷ 割込み処理プログラム
次に、割込みが発生した場合に実行する処理のプログラムも記述する必要があります。
割込み処理プログラムは、関数名が決まっていて、次のように記述します。
void __interrupt() isr(void)
{
// 割込み処理プログラム内容を記述する
// 割込み処理の最後に割り込みフラグをクリアする
}
void __interrupt() isr(void)
は割り込みが発生したときに行う処理の関数で、このように書くとXC8コンパイラはこの処理は割り込み処理だな、と認識してくれます。なお、関数名に関しては他の名前でも構いません。
ただ、PICマイコンプログラムの割込み処理関数は、一般的にisr
としているようです。
そのため、このシリーズでもisr
を使用することにしました。(isr
は「interrupt service routine」の略で「割込み処理プログラム」の意味です)
上のプログラムでは、割込み処理が終わったら、最後に割込み処理フラグをクリアします。
「割込み処理フラグをクリア」と言っても良くわかりませんよね。
そこで、この「割込み処理フラグ」について簡単に説明します。
「ピンの入力値変化」などの割込みが発生した場合、割込み処理プログラムが実行されますが、その際、どの割込みが発生したか「フラグ」と呼ばれるレジスタ値が1にセットされます。
例えばRA3ピンの入力値変化により割込みが発生した場合は、IOCAF3
というレジスタが1にセットされます。
「フラグ」とは「旗」の意味です。
レジスタの1ビットを旗に見立てて、0が旗が降りている状態、1が旗が上がっている状態で、フラグが1か0か、つまり旗が上がっているのか降りているのかでどの割込み処理が発生したかわかるようになっています。
ネットでたまに「何とかフラグが立った」という表現を見かけますが、おそらくここからきているのではないかと思います。
また、フラグに0あるいは1をセットすることを、一般的にはそれぞれ「フラグをクリアする」「フラグをセットする」と呼んでいます。
割込み処理プログラムでは、最後にこの割込み処理が発生したフラグをクリアする必要があります。
もしこのフラグをクリアしないと、割込み処理が終わっても、割り込み処理フラグだ立ったままですので(割り込みが発生している状態のままですので)再度割込み処理プログラムが呼ばれてしまいます。
割込み処理プログラム例
前回、LEDを1秒ごとに10回点滅させるプログラムを作成しました。
このプログラムの「LEDを1秒ごとに10回点滅する」という処理部分は変更せずに、「RA3ピンのスイッチが押されたら、高速にLEDを点滅する」という割込み処理を追加したプログラムを作成してみます。
割込み処理の追加は以下の内容を追加する必要があります。
- 割込み処理の有効化
- 割込み処理が必要ない場合は無効化
- 割込み処理プログラム
- ❸-1. 割込み処理プログラム本体
- ❸-2. 割込みフラグクリア
XC8コンパイラのVersion1.xxを使用している場合、割り込み処理関数の書き方が異なりますので注意してください。
Version1.xxでは割り込み処理関数を次のよう記述します。
void interrupt isr(void)
Version1.xxを仕様する場合、今回作成したプログラムの割り込み処理関数記述は上のように変更してください。
これらの内容を追加したプログラムを以下に示します。なお、プログラム内に上の番号も記載しましたので参考にしてみてください。
次のプログラムでは、割り込み処理で__delay_ms
関数によりかなり時間がかかる処理をしています。(全体で1.2秒程度)
実際の割り込み処理は、このように時間のかかる処理は避けたほうが良いです。
主な背景としては、メインの処理が長時間中断されたり、他の割り込みを許可している場合にその割り込みが処理できない、などの理由があります。
/*
* PICマイコン電子工作入門 応用編 第18回
* 割り込み処理構造理解のためのサンプルプログラム
* LEDを1秒間隔で10回点滅後、アラームを鳴らす
* もしスイッチが押されたらLEDを早く点滅する
*/
#include <xc.h>
// PIC12F1822 Configuration Bit Settings
// CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
// クロック周波数指定
// __delay_ms()関数が使用する
#define _XTAL_FREQ 1000000
//
// メイン関数
//
void main(void) {
// PICマイコン設定
OSCCON = 0b01011010; // 内部クロック周波数を1MHzに設定
ANSELA = 0b00000000; // 全てのピンをデジタルに設定
TRISA = 0b00001000; // RA3を入力、それ以外は出力に設定
// ブザーをOFFにする
LATA4 = 0;
// -----------------------
// (1) RA3割り込み有効化
// -----------------------
IOCIE = 1;
IOCAN3 = 1;
GIE = 1;
// LED点滅開始
for(uint8_t i=0; i<10; i++) {
LATA5 = 0;
__delay_ms(1000);
LATA5 = 1;
__delay_ms(1000);
}
// ---------------------
// (2) 割り込み無効化
// ---------------------
GIE = 0;
IOCIE = 0;
IOCAN3 = 0;
// アラーム音を3回鳴らす
for(uint8_t i=0; i<3; i++) {
// ピッ
LATA4 = 1;
__delay_ms(70);
LATA4 = 0;
__delay_ms(70);
// ピッ
LATA4 = 1;
__delay_ms(70);
LATA4 = 0;
__delay_ms(70);
// ピーッ
LATA4 = 1;
__delay_ms(70);
LATA4 = 0;
// 1回分の音パターンの間を少し空ける
__delay_ms(800);
}
// 動作停止
while(1){
}
}
// ---------------------------
// (3) 割り込み処理プログラム
// ---------------------------
void __interrupt() isr(void)
{
// ------------------------------------------
// (3-1) 割込み処理プログラム
// 割り込み処理が実行されたことを表現するために
// LEDを点滅させる
// ------------------------------------------
// LEDが点灯中に割り込みが発生することもあるので、
// 少しの間LEDをOFFにする
// ただし、割り込み処理内で時間がかかる処理を入れるのは本来は避けるべきです
LATA5 = 0;
__delay_ms(300);
// 短く3回点滅
for( uint8_t i=0; i<3; i++) {
LATA5 = 0;
__delay_ms(100);
LATA5 = 1;
__delay_ms(100);
}
// 少しの間LEDをOFFにする
// この処理も時間がかかるので本来は避けるべきです
LATA5 = 0;
__delay_ms(300);
// --------------------------
// (3-2) 割込みフラグクリア
// --------------------------
IOCAF3 = 0;
}
更新履歴
日付 | 内容 |
---|---|
2017.9.3 | 新規投稿 |
2018.12.2 | レジスタ名の補足説明追加 プログラムテンプレートをMPLABX IDE v5.10のものに変更 |
2019.2.8 | 割り込み処理関数宣言をXC8コンパイラVersion2.00以降の書式に変更 |
2025.5.23 | 割り込み処理の内容を変更(LED点滅がわかりやすくなるように変更) XC8 Version3.00で動作確認 |
お世話になります。割り込み処理で質問があります。
ピンへの入力がhigh(5V)の時に割り込みさせたい場合は、IOCAP1=1とかにすればいいと思っていたのですが、IOCAP1=1の設定で、ピンへの入力がないのに割り込み処理が走ります。ピンをGNDに接続すると割り込みは起きなくなります。
何かTRISA以外の設定も必要なのでしょうか?
ご質問どうもありがとうございます。
ピンの入力が 0→1 に変化した時に割り込みを発生させたい場合は、IOCAP1レジスタを1に設定すれば動作するはずです。設定としては以下のようになります。
TRISA1 = 1 : RA1ピンを入力ピンに設定
IOCAP1 = 1 : RA1ピンが0→1に変化した時に割り込み発生するように設定
IOCIE = 1 : ピンの変化による割り込みを許可する
GIE = 1 : 割り込みを許可する
「GNDに接続すると割り込みは起きなくなる」とのことですので、「入力がないのに割り込み処理が走る」というのはおそらくRA1ピンに何も接続されていないことが原因のように思います。
RA1ピンを入力ピンにした場合、ピンに何も接続されていない状態では、RA1ピンの入力は0か1か確定しません。「入力ピンに何も接続しない」というのは「入力が0」ということではありませんのでご注意ください。
RA1ピンにスイッチを接続して、0→1に変化した時に割り込みを発生させたい場合、RA1ピンにプルダウン抵抗を入れる必要があります。なお、プルダウン抵抗の入れ方がわからないようでしたら以下の記事もご参考にいただければと思います。
https://tool-lab.com/pic-basic-24/
早々の返信ありがとうございます。
もっと後の記事に出ていたんですね。
プルダウン抵抗入れて、うまく動作しました。
ありがとうございます。
動作したようでよかったです!
ところで、先ほどのコメント欄でご案内した記事は「基礎編」の第24回の記事になります。
現在お読みいただいている割り込み処理などの記事は「応用編」の記事になります。応用編は基礎編の内容を前提として書いていますので、もし基礎編をお読みでない場合、一度基礎編をご確認いただけると応用編の理解が深まると思います。
大変やさしくて詳しい解説を有り難う御座います。
割り込み処理をしっかりと理解しようと第16回からプリントアウトして読んでいます。
この回のサンプルプログラムを手打ちで入力して,作動させたましたが、
RA3スイッチを押すと、カウントは止まりますが割り込み関数の高速点滅をしません。
プログラムリストとにらめっこして点検しましたが,間違いは無いはずでした。
サンプルプログラムをコピペして作動させると、しっかりと割り込み関数の点滅をします。
両方のリストをプリントアウトしてつき合わせてみました。
有りました。 コンフィギュレー設定で、「VLP」設定が、「ON」になっていました。まさか思いながら、OFFに変更して、割り込み関数が多々しく動作する様になりました。
ゴチャゴチャ書きましたが、このコンフィギュレーション設定が、割り込み関数に関係ストは思えないのですが、如何でしょうか。
よろしくお願い致します。
コメントいただきどうもありがとうございます。
申し訳ございません、英語系のサイトを含めて検索してみたのですが、
LVPと割り込み処理の関係についての記事は見つかりませんでした。
原因を考えてみたのですが、なかなか思い当たらない、というのが現状です。
LVPについてわかりづらいかもしれませんが簡単にご説明します。
LVPはLow Voltage Programming = 低電圧プログラミング(書き込み)の略です。
PICKitを使ってPICマイコンにプログラムを書き込む際、PICKitはVppピンに
高い電圧(9V以上)をかけて書き込みをしています。ただ、プログラム書き込み器を
自作する場合など、高い電圧を発生する回路を作るのは面倒ですので、
最近のPICマイコンにはLVP機能が搭載され、PICマイコンの電源電圧で
プログラムの書き込みができるようになっています。例えば3.3V動作のPICマイコンでは、
Vppの電圧は3.3Vで書き込みができるようになっています。
PICマイコン側のLVP設定のデフォルトはONになっています。
このようにLVPの設定は、単にプログラムの書き込みの時の話ですので、
割り込みに関係する点はなさそうです。
ただ、LVPをOFFに変更する、つまり高い電圧で書き込みをすることで
動作した、というのは、LVPがONの場合、何かの書き込みができない、
あるいは失敗しているのかもしれません。
調べた範囲、考えた範囲ではここまでの情報になってしまいます。
申し訳ございません…
早速の回答頂き、有り難う御座います。
割り込み処理関数とコンフィギュレーション設定には特に関係ないとのことで,
一安心です。 改めて基礎編のコンフィギュ項目を読み返しました。
マイクロチップ社のIDEや無料コンパイラーは可成り癖があるとも聞きます。
又、私のパソコンもどうも勝手な動きをしている気配が感じられます。
今回の件は不思議ですが、今後また状況が変わるかもしれませんので、PICマイコンを使うときは頭の片隅に置いておこうと思います。
コメントいただきどうもありがとうございました。
本件、LVP = ONにすると、MCRLEの設定に関係なくRA3はMCLR機能に固定されるからです。
コメントいただきどうもありがとうございました。
LVP=ONにする
↓
プログラム書込みモードに移行するための高電圧印加は使わない
↓
MCLR信号を使ってプログラム書込みモードに移行するしかない
↓
LVP=ONの場合は強制的にRA3はMCLR機能に固定される
という感じでしょうかね。
情報いただきどうもありがとうござまいした!
勉強になります!
お世話になっています。
このプログラムの割り込みフラグをクリアは
IOCAF = 0;
となっていますが、
IOCAF3 = 0;
ではないでしょうか。
それとも番号を付けないとAポート全体を意味するのでしょうか。
すみません。投稿先を間違いました。19回の内容です。
IOCAFのレジスタ内容をデーターシートで確認し、自己解決しました。
割り込みスイッチを複数付けてどれが押されたなど便利そうですね。
ご連絡が遅くなりすみませんでした。
すでに解決されたとのことでよかったです。
ご指摘どうもありがとうございました。第19回の「IOCAF = 0;」は「IOCAF3 = 0;」にしたほうがいいです。すみませんでした。後ほど訂正しておきます。
なお、コメントいただいている通り、IOCAFフラグは入力設定可能なピンを個別に判定できますので、押されたスイッチに応じて処理を変える、ということができます。スイッチ処理は割り込みで処理すると楽ですので、何かご自身で作られるときに活用してみてください。
初めまして,
最近PICをいじりはじめたものです.
示されたものをコピペして,XC8(v2.05)でコンパイルすると
つぎの3ヵ所でエラーになってしまいます.
#include
for(unsigned char i=0; i<10; i++) {
for( unsigned char i=0; i<2; i++) {
forのところは i<10 などとすればよいことがわかったのですが,
調べてみると普通のc言語では説明に出て来るので,picで使えないと言うことでしょうね.
の意味が分かりません.これを削除するとOKのようです.
作りたいのは,よくクリスマスイルミネーションで,ボタンを押すごとに異なるパターンで
LEDが点滅するものです.
こちらを応用すればできるような気がしたのですが…
よろしくご教授お願いいたします.
shoさま、
コメントどうもありがとうございました!
ご指摘いただいた部分は間違ってます。修正しました。すみませんでした。はhtmlタグと判断されて、勝手に「 でクローズされてしまうようです…意味不明です…)。そのため、他の記事でも同様のことが発生していると思いますので、もしコードをビルドしてもエラーになる、ということがありましたらこれが原因と思われます。これから全部の記事のコードを修正していこうと思いますが、、、ちょっと時間かかりそうです。
実は、この記事の原稿では正しいコードになっているのですが、このサイトで使用しているWordPressに記事を投稿するときに「<」などが変換されてしまうようです(「
PICマイコンは普通のC言語を使用できますので、普通のC言語の書籍を参考にしていただけれぱ大丈夫です。
クリスマスイルミネーションを製作されるということで、自分で仕様を決められるのはとても楽しいですよね。色々な光り方をプログラムして、それをボタンで選択するのは面白いですよね。
・パターン選択
書かれている通り、割り込み処理でボタンを検知してパターンを変えるのがいいと思います。PICマイコンの普段の処理はイルミネーション点灯パターンの制御をしていますので、ボタンの検知をするのはちょっと大変です。そこで、IOCの割り込み検知を使うのがスマートだと思います。また、ボタンも複数用意して、点滅のパターンを変えるボタン、スピードを変えるボタンなどかあると色々と細かい制御ができそうですよね。世界一細かい制御ができるクリスマスイルミネーション、っていうのも面白いと思います!
・使用するLED
クリスマスイルミネーションですと、LEDの数が多い方がいいと思います。普通のLEDをPICマイコンに接続しても10個〜20個ぐらいが回路的にも限界ですが、「WS2812B」というLEDを使用したテープLEDがオススメです。amazonで「WS2812」で検索すると色々出てきますが、300灯(LED300個)で3500円程度です。数が多くてちょっと高めですが、好きなところでハサミで切って使うことができます。またPICマイコンの1本の信号線で、1個1個、個別に1677万色の指定ができます。「WS2812」を使った制御の様子はYoutubeで動画などもありますので探してみてください。
他に不明点ありましたらご質問いただければと思います。
庄司です.
お忙しい中(多分,この分野のお仕事?),すみやかな回答をいただき,恐縮しております.
そういうことがあるのですね.
全部を直すのは骨だと思います.
なにか,指摘があったらでよろしいかと思います.
どうもありがとうございます.
その他の記事も,大変参考になりますし,
原理を詳しく解説して頂いているので,本当に助かっています.
70歳で始めたPICです.
また,なにかありましたらご支援よろしくお願いいたします.
ありがとうございました.