計数器プログラムはどのような仕組みで実現されているのでしょうか? 詳しく見ていくことにしましょう。
計数器プログラムでは、「押しボタンからの入力検出」と「カウント値のLEDへの表示」をどうするかがポイントになります。まず押しボタンからの入力検出から始めましょう。
Teaboardの右のボタンはデジタル入出力ポートCの5番ピン、左のボタンは4番ピンに接続されています。デジタル入出力ポートCの状態は、in_w()(32bit幅のI/O読み出しマクロ)で0x0021c224番地を読み出すことで分かります。ボタンが押されていない状態が“High”(1)で、押されているときだけ“Low”(0)になります。
そこで例えばリスト3のようにすれば、ボタンの状態を監視することが可能です。このように、ループしながら常に監視を続けることを一般に「ビジーループ(busy loop)」と呼びます。
for(;;) { /* 無限にループする */ x = in_w( 0x0021c224 ); /* デジタル入出力ポートCからの入力の読み出し */ if ((x & (1 << 4)) == 0) { printf( "左ボタンは押されています\n" ); } else { printf( "左ボタンは押されていません\n" ); } if ((x & (1 << 5)) == 0) { printf( "右ボタンは押されています\n" ); } else { printf( "右ボタンは押されていません\n" ); } }
リスト3 ビジーループによる押しボタンの検出 |
しかし、ビジーループには大きな欠点がいくつかあります。図3を見てください。
押しボタンが押された時刻はA、B、Cの3回です。しかし、ループによって一定時間間隔で監視しているため、実際に検出する時刻はA'、B'のように遅れが生じてしまい、ボタンが押された瞬間にリアルタイムに反応することができません。しかもCでは、ボタンが押されていた時間が短いため、押されたことを検出できずに取りこぼしてしまっています。
また、ボタンからの入力がHighからLowに変わる状態変化(エッジ)を検出するだけでいいのに、常に100%のCPUパワーを使って状態(レベル)を監視し続けるのは、CPUパワーの観点からも無駄です。その間はほかの処理ができません。CPUパワーはもっとほかの処理に振り向けた方が経済的です。
入力をリアルタイムに処理したい場合は、「割り込み」(interrupt:インタラプト)を利用します。割り込みには、ビジーループのような欠点がありません。
ボタンなどの外部機器からの入力信号は、CPUの代わりに割り込みコントローラ(またはI/Oコントローラ)という専用のハードウェアに任せます。これにより、CPUは入力信号の状態を気にせず、入力が発生するまでの間は別の処理に専念できます。
入力が発生すると、割り込みコントローラ経由でCPUに割り込みが入ります。すると、CPUはそれまで行っていた処理を一時中断して、即座に割り込み処理のためのルーチン(割り込みハンドラ)を実行します。割り込みハンドラの処理が終わると、CPUは元の処理を継続します。
割り込みはリアルタイム処理には必須の機能といえるでしょう。
実際には、1つのCPUチップの中にCPUコアと割り込みコントローラ(またはI/Oコントローラ)が内蔵されている場合がほとんどです。しかし概念的には、CPUと割り込みコントローラは区別されます。
T-Kernelで割り込みハンドラを登録するAPIは、tk_def_int()です。リスト1では、左ボタンの割り込み定義番号である164に対して割り込みハンドラint_left()を登録しています。同様に、右ボタンの割り込み定義番号である165に対して割り込みハンドラint_right()を登録しています。
T_DINT d_left = { TA_HLNG, int_left }; T_DINT d_right = { TA_HLNG, int_right }; (中略) /* 左ボタンに対する割り込みハンドラの定義 */ tk_def_int( 164, &d_left ); SetIntMode( 164, IM_EDGE | IM_LOW ); ClearInt( 164 ); EnableInt( 164, 0 ); /* 右ボタンに対する割り込みハンドラの定義 */ tk_def_int( 165, &d_right ); SetIntMode( 165, IM_EDGE | IM_LOW ); ClearInt( 165 ); EnableInt( 165, 0 );
リスト1の割り込みハンドラ定義部 |
TA_HLNGは、割り込みハンドラをアセンブラではなく高級言語(C言語)で書くという指定です。
割り込みの発生条件としては、SetIntMode()で「IM_EDGE | IM_LOW」を指定しています。これは、HighからLowになった瞬間(エッジ)に割り込みを発生させることを示します。また、EnableInt()で割り込みを許可しています。
割り込みが発生して割り込みハンドラのint_left()やint_right()が呼ばれたら、ClearInt()で割り込み要求をクリアします。これを行わないと、割り込みハンドラが終了した瞬間に再び割り込みが発生した状態となり、これを繰り返すことになってしまいます。
ボタンが押されたことを割り込みで検出したら、カウントをアップして7セグメントLEDにカウントを表示するわけですが、それはどのように行えばいいでしょうか?
Teaboardの7セグメントLEDは、左(10の位)が0x16100002番地、右(1の位)が0x16100000番地に接続されています。これらの番地にデータを書き込むことで、LEDを制御できます。7セグメントLEDは、その名のとおり7本のLEDで1けたの数字を表示するデバイスです。厳密には、図4のように小数点も含めて8本のLEDがあり、ちょうど8bitの2進数の各bitに対応しています。
各bitは「1」が消灯、「0」が点灯ですので注意してください。例えば、1の位に「4」を表示させるには、セグメント「a」「d」「e」「点」の4本を消灯し、残り(「b」「c」「f」「g」)を点灯すればいいので、0x01+0x10+0x08+0x20=0x39を0x16100000番地に書き込みます。
0から9までの数字を表示させるには、それぞれ0x24、0x3f、0x62、0x2a、0x39、0xa8、0xa0、0x3e、0x20、0x28を書き込めばいいことになります。これはリスト1の関数set_led()で行っています。
/* 7セグメントLEDの表示 */ void set_led( int x ) { int p[] = {0x24, 0x3f, 0x62, 0x2a, 0x39, 0xa8, 0xa0, 0x3e, 0x20, 0x28 }; out_h( 0x16100002, p[x / 10] ); /* 10の位の表示 */ out_h( 0x16100000, p[x % 10] ); /* 1の位の表示 */ }
リスト1のset_led()関数 |
Copyright © ITmedia, Inc. All Rights Reserved.