検索
連載

割り込みと7セグメントLEDの制御T-Engineプログラミング入門(2)(3/4 ページ)

リアルタイム処理に欠かせない「割り込み」の使い方と、デジタル時計などで見かける7セグメントLEDで数字を表示する方法を解説

PC用表示 関連情報
Share
Tweet
LINE
Hatena

計数器プログラムの詳細

 計数器プログラムはどのような仕組みで実現されているのでしょうか? 詳しく見ていくことにしましょう。

ビジーループによるボタン入力の検出

 計数器プログラムでは、「押しボタンからの入力検出」と「カウント値の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を見てください。

ビジーループの欠点
図3 ビジーループの欠点

 押しボタンが押された時刻はA、B、Cの3回です。しかし、ループによって一定時間間隔で監視しているため、実際に検出する時刻はA'、B'のように遅れが生じてしまい、ボタンが押された瞬間にリアルタイムに反応することができません。しかもCでは、ボタンが押されていた時間が短いため、押されたことを検出できずに取りこぼしてしまっています。

※コラム:レベルセンスとエッジセンス
信号線が「Highであること」(または「Lowであること」)という状態そのものを検出することを「レベルセンス」(level sense)といいます。
一方、「HighからLowへ」(または「LowからHighへ」)の状態変化の瞬間を検出することを「エッジセンス」(edge sense)と呼びます。


 また、ボタンからの入力がHighからLowに変わる状態変化(エッジ)を検出するだけでいいのに、常に100%のCPUパワーを使って状態(レベル)を監視し続けるのは、CPUパワーの観点からも無駄です。その間はほかの処理ができません。CPUパワーはもっとほかの処理に振り向けた方が経済的です。

「割り込み」登場

 入力をリアルタイムに処理したい場合は、「割り込み」(interrupt:インタラプト)を利用します。割り込みには、ビジーループのような欠点がありません。

 ボタンなどの外部機器からの入力信号は、CPUの代わりに割り込みコントローラ(またはI/Oコントローラ)という専用のハードウェアに任せます。これにより、CPUは入力信号の状態を気にせず、入力が発生するまでの間は別の処理に専念できます。

 入力が発生すると、割り込みコントローラ経由でCPUに割り込みが入ります。すると、CPUはそれまで行っていた処理を一時中断して、即座に割り込み処理のためのルーチン(割り込みハンドラ)を実行します。割り込みハンドラの処理が終わると、CPUは元の処理を継続します。

 割り込みはリアルタイム処理には必須の機能といえるでしょう。

 実際には、1つのCPUチップの中にCPUコアと割り込みコントローラ(またはI/Oコントローラ)が内蔵されている場合がほとんどです。しかし概念的には、CPUと割り込みコントローラは区別されます。

※コラム:割り込み遅延時間
厳密には、割り込みが発生すると即座に割り込みハンドラが実行されるのではなく、若干の遅れがあります。この時間差が生じる原因は、ハードウェアの制約のほか、下記のようなOSの仕様にもあります。T-KernelをはじめとするリアルタイムOSは、これらの遅延時間をなるべく短く抑えてリアルタイム性を保証するように、さまざまな工夫が凝らされています。
1.OS内部の割り込み禁止区間
OS内部では、中断されると困る一連不可分な内部処理(クリティカルセクション)を実行する間は、割り込みを禁止しています。ちょうどそのときに割り込みが発生した場合は、クリティカルセクションから抜けるまで、割り込みハンドラの実行が遅延されます。
2.レジスタの待避時間
割り込みハンドラに実行を移す前に、それまで行っていた処理で使っていた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への表示

 ボタンが押されたことを割り込みで検出したら、カウントをアップして7セグメントLEDにカウントを表示するわけですが、それはどのように行えばいいでしょうか?

 Teaboardの7セグメントLEDは、左(10の位)が0x16100002番地、右(1の位)が0x16100000番地に接続されています。これらの番地にデータを書き込むことで、LEDを制御できます。7セグメントLEDは、その名のとおり7本のLEDで1けたの数字を表示するデバイスです。厳密には、図4のように小数点も含めて8本のLEDがあり、ちょうど8bitの2進数の各bitに対応しています。

7セグメントLED
図4 7セグメントLED

 各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.

ページトップに戻る