マイコンを活用するための周辺機能の仕組みや使い方を解説しています。今回はマイコンを効率良く使うために欠かせない「割り込み」について解説します。

作業効率を高める「割り込み」とは?

本シリーズの第2回目「タイマ編」で簡単にお話した、「割り込み」の概念をもう一度説明しましょう。「沸騰したお湯に卵を入れ、ゆで卵が出来るまでの10分間、何度も時計を確認する」、このような経験は誰にもあると思います。マイコンの世界でも同様に、何かの状況が達成されるのを待つとき、対象を定期的にチェックする方法があります。例えば、GPIO(汎用I/Oポート)への入力が0から1に変化するのを待つときに、GPIOの状態を一定の間隔でチェックするプログラムです。このような処理は「ポーリング」と呼ばれています。

ポーリングは、状況の変化を知るための簡単な方法ですが、調べる頻度が低い(間隔が長い)と変化を見逃すことになり、頻度が高すぎる(間隔が短い)と調べても変化が無い「空振り」になります。簡単なプログラミングで済む処理のため、対象の変化の頻度が判っているときには有効です。しかし、何度も調べに行くことで、マイコンに負荷をかけることになり、消費電力面でも不利になります。

そこで用いられるのが、今回紹介する「割り込み」を使った方法です。割り込みが発生すると、CPUは実行している仕事を一時停止して、別の仕事を行います。別の仕事が「割り込んで」来る、といったイメージですね(図1)。割り込んできた仕事が終わると、CPUは元の仕事へと戻ります。

図1:割り込みとポーリング

図1:割り込みとポーリング

仕事中にゆで卵を作るとしましょう。仕事の手を止めたくないので、卵をお湯に入れたらタイマをセットして仕事を続け、10分後にタイマが鳴ったら卵をお湯から引き上げます。この場合、タイマが鳴ることが割り込みであり、割り込んできた仕事が「卵をお湯から引き上げる」ことです。割り込みに関してはこのようなイメージで理解しましょう。

マイコンでの割り込み処理

割り込みは、マイコン内部と外部のさまざまな機器から発生します。スイッチやセンサなどマイコン外部からの割り込みは、外部端子割り込みと呼ばれ、それらの機器からの割り込み信号を「IRQ」と名がついたピンで受信し、割り込みコントローラ(RX63Nでは「ICUb」と呼称)に通知します。IRQとは「Interrupt ReQuest」の略で「割り込み要求」を意味します。一方、マイコン内部にあるタイマやGPIO、シリアル通信デバイスUARTなどの周辺機器からの割り込みは、周辺機器割り込みと呼ばれ、割り込み信号は各々の周辺機器から直接、割り込みコントローラに通知します。

割り込みコントローラではさまざまな機器からの割り込み信号を、優先順位に基づいて適切な順序でCPUに伝えます。また、割り込みを無効に設定している機器からの割り込み信号を、CPUに伝えず、無視する(マスクする)こともできます。CPUは割り込みコントローラから受け取った指示に従って、対応したプログラム(割り込み処理)を実行します。

割り込みコントローラからの割り込み信号をCPUが受信すると、まず、実行中のプログラムを止めます。そして、「どこから再開するか」といった復帰のための情報が自動的に保存されます―「待避」と呼びます。待避完了後、割り込みにより実行するプログラムが始まります。これが終了するときに、待避した情報をCPUに戻します―「復帰」と呼びます(図2)。待避と復帰は、CPUが自動的に行いますから、プログラマが手順などを気にする必要はありません。

図2:割り込み処理の流れ

図2:割り込み処理の流れ

例えば、UARTでシリアル通信を行っているとき、文字が受信されたかを常に見張っているのは効率が良くありません。多くの場合は、情報が届いたときに割り込みを発生させ適切な処理をするようにプログラムを組みます。また、タイマに割り込みを発生させることも多くあります。一定時間経ったら何かをする、といった処理の時に、タイマからの信号で処理が始まるようにプログラムします。このようにマイコンを活用する上で、割り込みは大変役立ちます。

難しい割り込みプログラムも、ライブラリを使ってお試し可能

割り込みに関するプログラムを書くためには、対象となるマイコンの動作を理解する必要があります。深い理解が必要なため、簡単に試すわけにいきません……が、幸い、本シリーズでお馴染みのGR-SAKURAのライブラリには外部端子割り込みに関する処理がありますので、これを用いて試すことが可能です。

GR-SAKURAに外部端子割り込みを与えるためには少し工作が必要です。GR-SAKURAではIO30ピンからIO35ピンが外部からの割り込み信号を受け付けます。今回は、IO0ピンにタイマ出力を導き、これをIO31ピンに送り割り込み信号としました。そのため、IO30からGNDに至る部分にピンソケットを立て、ブレッドボード用のワイヤーでIO0とIO31を接続しました(図3)。

図3:GR SAKURAの準備(ピン配置)

図3:GR SAKURAの準備(ピン配置)

サンプルプログラム(図4)は、GR-SAKURAの外部割り込み信号入力ピン(IO30からIO35のうちの1つ)への入力がLからHに変わると、LEDの灯りが動く、というものを用意しました。先に説明した「外部端子割り込み」に相当する割り込みを検出して動きます。IO0ピンからタイマ出力を行い、これと割り込み入力ピンであるIO31ピンにつなぐ、という方法で一定時間毎に割り込みが起きるようにしました。

※このプログラムをコンパイルしても、前出のIO0とIO31の接続を行っていないGR-SAKURAでは動作しません。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
 
 
/*GR-SAKURA Sketch Template Version: V1.08*/
#include <rxduino.h>
 
#define INTERVAL 1
 
int i = 0;
 
void irq3() //IO31ピンへの割り込み入力時にこの関数が起動、LEDを順番に点滅する処理を担当
{
    switch( i)
    {
        case 0:
            digitalWrite( PIN_LED3, 0); //LED3を消灯
            digitalWrite( PIN_LED0, 1); //LED0を点灯
            i++;
            break;
        case 1:
            digitalWrite( PIN_LED0, 0); //LED0を消灯
            digitalWrite( PIN_LED1, 1); //LED1を点灯
            i++;
            break;
        case 2:
            digitalWrite( PIN_LED1, 0); //LED1を消灯
            digitalWrite( PIN_LED2, 1); //LED2を点灯
            i++;
            break;
        case 3:
            digitalWrite( PIN_LED2, 0); //LED2を消灯
            digitalWrite( PIN_LED3, 1); //LED3を点灯
            i = 0;
            break;
    }

 
void setup() //出力先のピン設定、LEDの初期状態は消灯と設定、割り込み時に動作する関数の設定、タイマの周波数設定を実施
{
    pinMode(PIN_LED0,OUTPUT);   //GPIOはLED0へ信号出力すると設定
    pinMode(PIN_LED1,OUTPUT);   //GPIOはLED1へ信号出力すると設定
    pinMode(PIN_LED2,OUTPUT);   //GPIOはLED2へ信号出力すると設定
    pinMode(PIN_LED3,OUTPUT);   //GPIOはLED3へ信号出力すると設定
    pinMode(PIN_P21, OUTPUT);   //タイマ出力に使うIO0ピンも出力用に設定
 
    digitalWrite( PIN_LED0, 0);  //LED0を消灯する
    digitalWrite( PIN_LED1, 0);  //LED1を消灯する
    digitalWrite( PIN_LED2, 0);  //LED2を消灯する
    digitalWrite( PIN_LED3, 0);  //LED3を消灯する
 
    attachInterrupt( 3, irq3, RISING);   //IO31(IRQ3)が割り込み受付時に呼ぶ関数がirq3()であると設定
 
    tone(PIN_P21, INTERVAL, 0);   //IO0よりINTERVALで定めた周波数で出力する
}
 
void loop()    // loop()の中の処理はない
 {
 }
 

このライブラリでは、前出の割り込み信号入力ピンのそれぞれに対応した処理を定義できます。今回は、IO31ピンへの入力の変化(LからHへの変化)によって割り込みを発生させるようにしています。どの入力ピンにどのような変化が起きたときに、どの関数を呼び出すかを定義したのが48行目にあるattachInterrupt()です。ここで、IO31ピンへの割り込み信号により、irq3()が起動される、と設定しました。このような設定は、setup()の中で一度定義するだけで、プログラム全体で有効です。他に、setup()の中では、タイマの定義、タイマ出力ピンの設定、LED出力の設定といった初期条件を記述します。

loop()関数による処理はありません。代わりに、irq3()という関数が仕事をしています。この関数は、プログラムのどこからも呼ばれているように見えません。しかし、これこそが、IO31ピンに割り込み信号が入った時にLEDの光を動かす部分なのです。irq3()では、4つのLEDのうち一つだけが点灯し、この関数が呼ばれると点灯しているLEDが一つ動くようにしています。「動き」を見せるために、caseラベルの部分で、GPIOからの出力で消灯と隣のLEDの点灯(端のLEDが点灯している場合、反対側の端のLEDについて点灯又は消灯)を行っています。

いつ起きるか分からない事象に対応するためには、割り込みは極めて有効な対応方法です。また、プログラムの無駄な動作も減り、消費電力の削減にも通じます。マイコンを本格的に活用するためには、必須の技術と言って良いでしょう。

次回からは二回に渡り、「そもそも、どのようにプログラムは動くか」ということをテーマに、プログラムの挙動をハードウェアとともに解説します。これまでのプログラミング例やハードウェアの解説で浮かんでくる疑問を一気に解消しましょう。