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;
<span class="red">_BIS_SR(GIE);</span>
for (;;) _BIS_SR(SCG1 | SCG0 | CPUOFF | OSCOFF); // goes into LPM4
return 0;
}
ポート1(の第3ビットの立ち下がり時の)割り込みを有効にする
今回はPort1 の bit 3 につながったタクトスイッチが押されたら
割り込みをかけます。MSP430の外部入力割り込みは立ち上がりエッジと
立ち下がりエッジが選択可能です。
P1IES
の該当ビットが0だと立ち上がり、
1だと立ち下がりに指定されます。タクトスイッチは回路図
P1IES レジスタの第3ビットは1にします。
割り込みはデフォルトでは有効になっていません。MSP430では2段階で
割り込みを有効にします。まずは、ペリフェラル毎の有効/無効設定です。
ポート1割り込みの場合、
<code>P1IE</code>の該当ビットが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_SR | SR |
| _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で定義されている割り込みの例
| 割り込み要因 | シンボル | ベクタアドレス |
|---|---|---|
| PORT1 | PORT1_VECTOR | デバイス依存 |
| PORT2 | PORT2_VECTOR | デバイス依存 |
| USI | USI_VECTOR | デバイス依存 |
| ADC10 | ADC10_VECTOR | デバイス依存 |
| TIMER A(CC0) | TIMERA0_VECTOR | デバイス依存 |
| TIMER A(CC1-2) | TIMERA1_VECTOR | デバイス依存 |
| Watchdog | WDT_VECTOR | 0xfff4 |
| NMI | NMI_VECTOR | 0xfffc |
ちなみに、以前の 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回にて。