LEDの制御に時間の概念を取り入れる:S08ではじめるマイコン制御プログラミング(4)(1/3 ページ)
少しずつ本格的になってきたマイコン制御。今回はタイマーと割り込みの考え方について、LEDの制御を例にしながら学ぼう。
前回はいろいろな方法でLEDを制御してみました。だんだん制御プログラムっぽくなってきましたね。皆さんも興味がわいてきたのではないでしょうか。今回はLEDの制御に時間の概念も加えてみましょう。
前回作ったアップ/ダウン・カウンタのプログラムはいかがでしたか。あれ? また不服そうですね。スイッチの反応がイマイチですって? 分かっていますよ。そういう反応が出るだろうと思っていました。今回はその問題の解決方法から順番に考えて行きます。一度に全部出したら話が続かなく……じゃなかった。一歩ずつ分かりやすく進んでいきますよ。
なぜ、スイッチの反応がおかしいのか?
なぜ、スイッチの反応がイマイチになってしまったのでしょう? プログラムのバグはなさそうです。一点気になるのがforループの中に入れた時間待ちの処理「wait(0xffff);」の呼び出し部分です。今回のテーマが「時間を操ってみる」ですので、ますます怪しいですね。前回のリスト6を見て、main関数にあるforループ内の該当処理を次のようにコメント・アウトしてみてください。
// wait(0xffff);
ここでちょっとした豆知識をご紹介します。CodeWarrior for MicrocontrollersのS08コンパイラでは、CプログラムでもC++で使う「//」のコメント記号が使えます。無償のSpecial Editionでコードサイズ32KBまで。C++言語と判断されて1KB制限なんてことはありません。
では、プログラムをビルドして実行してみましょう。LEDの点滅は早くて分かりませんがストップ/スタートでボタンの反応が良くなったのが実感できると思います。やはり原因はここにあるようです。
wait関数の内容をもう一度見てみましょう(リスト1)。引数として渡された回数だけ空のforループをしています。ここで時間が掛かっているのです。この処理中では、ボタンが押されてもプログラムはそれを知ることができません。このループの中に、スイッチの状態を調べる処理を入れれば良いのですが、せっかく機能単位で関数分けしたのに、それをすると、かえって読みにくいプログラムになってしまいます。どうすればよいのでしょうか……。実はいい解決方法があります。
割り込みの基本を押さえよう
ここで例え話をしましょう。想像してみてください。あなたは急いで書類をコピーしなければなりません。そんなときに限ってコピー機は別の書類を大量にコピーをしている最中です。しばらくあなたの順番は来そうにありません。辺りに人はいないようです。
あなたはコピー中の機械を強制終了させ、自分の書類のコピーを始めました。しばらくすると、何か刺すような視線を感じます。後ろを振り返るとすごい形相をした人が……。こうなると「○○さん、今日は一段ときれいですね。」などとおだてても、許してくれるどころかセクハラ扱いされるのがオチです。中断された大量コピーは、枚数やページ数を確認して再度、続きからコピーをとらねばなりません。今後の人間関係にも影響しそうな大事件です。
これはフィクションですが、十分に起こりえる状況です。このような事件を未然に防ぐため、多くのコピー機には割り込みボタンが用意されています。割り込みボタンで中断した作業は、割り込み処理終了後に元の処理を継続することができるのです。マイコンにはこれと同じ割り込みの機能が用意されています。この考え方を使ってカウンタのプログラムを改造してみましょう。
具体的には押されたスイッチの判定を行う部分を割り込み処理として抜き出します。マイコンは多くの割り込み(QE128の場合は32種類)をサポートしています。スイッチの入力で割り込みをかけるKBI(Keyboard Interrupt)もそのひとつです。発生した割り込みによってその処理は異なります。各割り込み処理は、そのアドレスをベクター・テーブルと呼ばれる領域に書いておく必要があります。
今回使うKBI割り込みは、エントリー番号18(アドレス0xFFDA-0xFFDB)に割り込み処理関数のアドレスを書くことになります。QE128のKBIは2つありますが割り込み処理は共通です。どのKBIの割り込みなのかは割り込み処理の中で判別する必要があります。
第2回で説明したようにひとつのピンにいくつもの機能が割り当てられているのです。KBIを使用するにはまず、KBIのピンに切り替えなければなりません。PTA2、PTA3、PTD2の各スイッチのKBIの対応を見てみましょう。リファレンス・マニュアルのTable7-1、7-2(140ページ)を確認してみてください。表1にスイッチとマイコンピンの対応表を示します。
スイッチ名 | ポートピン名 | KBIピン名 | |
---|---|---|---|
PTA2 | PTA2 | KBI1P2 | |
PTA3 | PTA3 | KBI1P3 | |
PTD2 | PTD2 | KBI2P2 | |
表1 スイッチとマイコンピンの対応表 |
KBIの割り込みピンに切り替えるレジスタは、KBIxPEです(xは1または2)。使用したいピンの該当ビットを1にします。また、KBIの制御用にKBIxSCレジスタもあります。ピンに信号が入力されるとKBFフラグが1になります。そのときに、KBIEビットを1にしていれば、割り込みが起きる仕組みです。続けて割り込みを受け付けるためにはKBFフラグをクリアしなければなりません。そのためには、KBACKに1を書きます(図1)。ちなみに、初期状態ではピンの信号の立ち下がりエッジを検出しています。
長々説明しましたがとてもシンプルに見えます。すぐにでもプログラミングできそうですね。でも、何か足りないとは思いませんか?
確かにKBI割り込みで割り込み処理を行うことはできそうです。しかし、どのスイッチが押されて、割り込みが掛かったのかはどうやって判定すればよいのでしょうか? KBFフラグではKBI1によるものかKBI2によるものかしか分かりません。リファレンス・マニュアルを見ても、それっぽいレジスタはなさそうです。
実はそれには、GPIOで使ったPTxDレジスタを使うのです。内部プル・イネーブル・レジスタ(PTxPE)も同様です。
KBI対応に改造したプログラムを見てみます。ここで触れていない関数はそのまま残して置いてくださいね。スイッチの判定部分は割り込み処理で行いますので、main関数の中から取り除きます。割り込み処理はchk_key_statという名前の関数にしました(リスト2)。
Copyright © ITmedia, Inc. All Rights Reserved.