ARMマイコン「LPC1114」には周辺機器としてタイマーが搭載されており、“割り込み”の概念を理解することで、複数のプログラムを並行しているかのように処理できます。
組み込みの世界で最も成功したプロセッサの1つ「ARM」を用いたマイコン開発にチャレンジするこの連載。これまではマイコン「LPC1114」に内蔵されているさまざまなペリフェラル(周辺機器)の使い方について勉強してきました。
それら周辺機器の多くは、外部とやりとりをするインタフェースとして利用することがほとんどでしたが、今回は周辺機器の1つでありながら、インタフェースとしては利用されない「タイマー」(図1)について取り上げます。
タイマーはLPC1114の周辺機器の1つではありますが、シリアルポートやGPIOのように外部へのインタフェースとしては使われません。時間経過を外部環境の変化とすると、その変化を把握するための「センサー」として使われます。
そして今回はもう1つ、重要なテーマとして「割り込み」があります。時間経過をイベントとして利用するタイマー割り込みの1つ「Ticker」についても取り上げます。
通常、CPUと周辺デバイスの関係は、常にCPUからの要求でデバイスが動作し、必要ならデバイスからの値を受け取るというものです。これとは逆に、デバイス側からCPUに対して要求をするのが「割り込み」です。割り込みが成立するためには、CPU側にもハードウェアとして対応できるための仕組みを備えている必要があります。
CPUと周辺デバイスの間は割り込み線でつながっており、割り込みを要求したい周辺デバイスはこの線をLow電位に落とします。するとCPUはいったん通常のプログラムを中断し、割り込みで要求された処理を実行します。CPUは割り込みを要求した周辺デバイスを特定し、そのデバイスとの間で取り決めたアドレスに実行が移ります。
このアドレスのことを「割り込みベクター」と呼びます。またこの割り込みベクターの先に書かれているプログラムのことを「割り込みサービスルーチン(ISR)」と呼び、ルーチンの最後は必ず割り込み復帰命令で終わる必要があります。この命令が実行されると割り込みシーケンスが終了し、もともと走っていたプログラムの中断した時点から実行を再開します。
なお、LPC1114の場合は周辺デバイスもCPUと同じパッケージに封入されているので、このCPUとデバイス間の割り込み線は見えません。
それでは割り込みが必要な理由を議論するため、以下の2つのサンプルプログラムを見ていきましょう。
1: #include "mbed.h" 2: DigitalOut myled(LED1); 3: int main() { 4: while(1) { 5: myled = 1; 6: wait(0.2); 7: myled = 0; 8: wait(0.2); 9: } 10: }
1: #include "mbed.h" 2: Serial pc(USBTX,USBRX); 3: int main() { 4: char c; 5: while(1) { 6: c = pc.getc(); 7: pc.putc(c); 8: } 9: }
list1は新規にプログラムを作成する際、templateを”Blinky LED Hello World”と選択すればテンプレートとしてmain.cppに自動生成されるソースコードです。発光ダイオードが0.2秒間点灯し、その後0.2秒間消灯します。これを永遠に繰り返すプログラムです。連載の第3回でも出てきました。回路図やブレッドボードの配線はそちらを参考にしてください(「mbed」で始めるARMマイコン開発入門(3):クラウド開発環境「mbed」の使い方とターゲットマイコンへの書き込み)。
List2はシリアルポート経由で受信した文字を、シリアルポートに対して送信するプログラムです。PCとシリアルポート経由で接続し、PC側でターミナルエミュレータなどのソフトウェアを立ち上げることで動作を確認できます。ターミナルエミュレータで打った文字がそのままPCの画面上に表示されます。ボーレートは特に指定していませんので、マイコン側はデフォルトの9600bpsになっています。うまく表示されない場合はターミナルエミュレータ側でボーレートの値を合せてください。詳しくは連載の第4回を参考にしてください(「mbed」で始めるARMマイコン開発入門(4):ARMマイコンに内蔵された周辺デバイスの使い方を学ぶ−シリアルポート編)。
この2つのプログラムを、1つのLPC1114で機能させることを考えて見ましょう。要は発行ダイオードを一定間隔で点滅させながら、PC側からはターミナルエミュレータでキーボードをたたくといつでも即座に文字が表示されるようなプログラムを作りたいわけです。
最大の難関は、list2の6行目のgetc()でしょう。この関数は文字が入力されるまで次の命令に実行が移らないタイプの関数なのです。ですからメインループの中にこの関数があるとキー入力があるまで止まってしまうため、発光ダイオードをある一定時間で点滅させようと思っても、発光ダイオードはその時点の状態のままになってしまいます。
というわけでgetc()を使わない方法を検討してみましょう。Serialのクラスライブラリーのリファレンスを見てみるとreadable()という関数があります。これは受信した文字がある場合は1、そうでない場合には0を返します。getc()と比べて都合がよいのは、いずれの場合でも値がすぐに返ってくる点です。この関数とgetc()を組合せば、文字を受信した時のみgetc()を実行し、それ以外の場合は別の処理に移ることが可能となります。
それではこの関数を用いて、プログラムを書き直して見ましょう。
Copyright © ITmedia, Inc. All Rights Reserved.