MSP430開発入門講座(2) GPIO割り込みでLEDをon/off

前回の問題点

前回は無限ループでスイッチの状態を監視し続けていました。 いわゆるポーリング(polling)というやつです。 こいつはいけません。CPUが無限ループを全力疾走して監視し続けるので 電力の無駄遣いです。処理内容によっては入力の取りこぼしが生じる 可能性もあります。 そもそもこんなことやってたら、ちょっと複雑なプログラムを書こうと 思ったら即、破綻します。 で、そんなときはもちろん『割り込み』です。(注: 大きめの OSを使うときは割り込みを直接使うことは滅多になくて、OS 側が提供する イベント処理の仕組み(signalとか)を使います。)

今回のソース

今回はちょっと仕様変更して、LEDの光り具合をトグル動作にしました。 ボタンを押す毎に赤と緑の点灯状態が切り替わります。 今回のポイントとなる(新しい)部分は赤で示してあります。それ以外は 第1回で解説済みです。

// tutorial02.c  by Yagshi 2011 (modified 2013)
#include <msp430.h>

void initializeMCU(void)
{
  WDTCTL = WDTPW + WDTHOLD;     // shut up the dog
  P1SEL &= ~(BIT0 + BIT6 + BIT3);
  P1DIR |= BIT0 + BIT6;
  P1DIR &= ~BIT3;
  P1REN |= BIT3;
  P1OUT |= BIT3;
  P1IES |= BIT3;                // H -> L edge
  P1IE  |= BIT3;                // enable interrupt
}

#pragma vector = PORT1_VECTOR
__interrupt interruptPort1(void)
{
  P1OUT ^= BIT0 + BIT6;
  P1IFG &= ~BIT3;   // clear flag
}

int main(void)
{
  initializeMCU();
  P1OUT |= BIT0;
  P1OUT &= ~BIT6;
  _BIS_SR(GIE);
  for (;;) _BIS_SR(SCG1 | SCG0 | CPUOFF | OSCOFF); // goes into LPM4
  return 0;
}

ポート1(の第3ビットの立ち下がり時の)割り込みを有効にする

今回はPort1 の bit 3 につながったタクトスイッチが押されたら 割り込みをかけます。MSP430の外部入力割り込みは立ち上がりエッジと 立ち下がりエッジが選択可能です。 P1IESの該当ビットが0だと立ち上がり、 1だと立ち下がりに指定されます。タクトスイッチは回路図
LaunchPad schematic (回路図より切り出した図)
を見ればわかるとおり、押すと立ち下がりエッジが生じるはずです。 したがって、P1IESレジスタの第3ビットは1にします。

割り込みはデフォルトでは有効になっていません。MSP430では2段階で 割り込みを有効にします。まずは、ペリフェラル毎の有効/無効設定です。 ポート1割り込みの場合、 P1IEの該当ビットが0で無効、 1で有効になります。ここまでのところを設定しているのが initializeMCU関数内の

P1IES |= BIT3;                // H -> L edge
P1IE  |= BIT3;                // enable interrupt

です。そして最後に CPUのSR(Status Register)内の GIE(Global Interrupt Enable) ビットを立てれば割り込みが有効になります。 今回はmain 関数内で以下のようにやっています。

_BIS_SR(GIE);

レジスタを直接操作することはC言語では基本的にできないので、 mspgcc では _BIS_SRというマクロが定義されています。これは (bis命令で)引数と SR の OR をとって SR に書き込むマクロです。 他にSRを操作するマクロには次のようなものがあります (詳細は msp430/include/iomacros.h 参照。なんでBISとBICだけ アンダースコアで始まるのかは良くわかりません…。)。

SR操作マクロ
マクロ意味
WRITE_SR(x)SR := x
READ_SRSR
_BIS_SR(x)SR := SR | x
_BIC_SR(x)SR := SR & ~x
以上、これで間違いなく割り込みが発生しますが、肝心の割り込み 処理そのものが まだでした。次節で割り込み処理の記述の仕方を説明します。

割り込み処理関数(ISR)の書き方

割り込みは本来 C 言語では記述しにくくて、その書き方は開発環境毎に いろいろ苦労が見られます。mspgcc の場合、何でもマクロにして ソースコードに直接記述できるようになっています。これはこれで すっきりしてて個人的には割と好きです。そんなわけで、 今回のソースコードの真ん中のあたり、

#pragma vector = PORT1_VECTOR
__interrupt void interruptPort1(void)

が割り込み関数の書き方になります。最初の 『#pragma vector = 割り込みベクトル』で、 どの割り込みベクトルに割り当てるかを決めています。今回はPORT1の 割り込みなのでPORT1_VECTORになります。 その次の『__interrupt interruptPort1(void)』が関数定義の 始まりです。interruptPort1 は単なる関数名なの好きなもので ok です。 主な割り込みベクトルの定義を次に示します。

関数の中では見てのとおり、port1の第0bitと第6ビットにつながった 赤と緑のLEDを反転させています。

mspgccで定義されている割り込みの例
割り込み要因シンボルベクタアドレス
PORT1PORT1_VECTORデバイス依存
PORT2PORT2_VECTORデバイス依存
USIUSI_VECTORデバイス依存
ADC10ADC10_VECTORデバイス依存
TIMER A(CC0)TIMERA0_VECTORデバイス依存
TIMER A(CC1-2)TIMERA1_VECTORデバイス依存
WatchdogWDT_VECTOR0xfff4
NMINMI_VECTOR0xfffc

ちなみに、以前の mspgcc では、ISR を次のように書きました。 今でも使えますが、その場合 include するのを msp430.h ではなく legacymsp430.h にする必要があります。

interrupt(PORT1_VECTOR) interruptPort1(void)
{   
}   

コンパイル〜実行 & おまけ

$ make tutorial02
msp430-gcc -Os -Wall -mmcu=msp430g2231   tutorial02.c   -o tutorial02
$ mspdebug rf2500 'prog tutorial02'

はい、思ったとおり動きました。…というかきれいに動きすぎ。 チャタリングが生じてもう少しおかしな動作になると思って いたのですが、ほとんど問題なしです。タクトスイッチって優秀なんですね。

次回予告

最後の無限ループに謎の繰り返し文があります。これは何でしょう。 そう、これは組み込み開発(に限ったことではなくてソフトウェア開発)に おいてとても重要なアイドル処理です。この1文を加えることで 消費電力が3ケタくらい変わってきます。解説は第3回にて。


by Yagshi 2013
『マイコンを使った開発について』に戻る

Valid XHTML 1.0!