「H8/3052F ITUで時間かせぎ」のページで、ITUタイマー機能を使った時間かせぎのプログラムをご紹介しました。時間かせぎはタイミング調整を行うためにとても重要な機能ですが、先のページでご紹介した機能では時間かせぎを行っている最中、マイコンは時間を計測するのに忙しくて他の仕事を一切できませんでした。ごく短い時間かせぎなら良いのですが、長い期間のインターバルを生成したい場合などは、非常に効率の悪い動きとなってしまいます。そこで割り込み処理の登場です。割り込み処理を用いることで本来の仕事に集中させて、必要な時だけ別の仕事を割り込ませることができるようになります。割り込み処理の発生要因は沢山準備されていますが、今回はITUタイマー機能の割り込み機能を使って、その動作について見ていきましょう。 1.割り込みとは マイコンのプログラムは、通常記述された命令をひとつずつ順番に実行していきます。しかし、折り込み済みの処理ではなく、どのタイミングで発生するかわからない処理に対応するにはどうしたら良いのでしょう。どのタイミングで発生するかわからないので、ずっと発生するのを待ち続けるというのもひとつの手です。しかしここでは、本来の仕事を処理しながら突然発生した仕事を割り込ませるという方法を使います。それが文字通り「割り込み」です。割り込みは、よく仕事中にかかってきた電話に例えられます。仕事中はその作業に専念していますが、電話のベルがなると仕事の手を止めて電話に出ます。そして電話が終わったら、再び先ほどの仕事の続きに戻ります。マイコンの世界でも、これと同じことを行います。割り込み要因が発生したら主となるプログラムの処理を一旦中断し、割り込みの発生要因にあわせた分岐先に飛んで必要な処理を行います。そして、その処理が終わったら、再び主となるプログラムの続きに戻ります。つまり、割り込みが発生するまでは、主となるプログラムの実行に専念できるのです。 2.割り込まないプログラム 割り込みを行うプログラムとの違いを分かりやすくするため、今回は先ず割り込まないプログラムを動かして見ましょう。今回のプログラムでは、二つの処理を行います。ひとつは、port2から入力されたBIT情報をそのままport1に出力させます。もうひとつは、port3の出力を1秒ごとに変化させます。port3の出力は正確に1秒ごとに変化させますが、port1の出力はport2の入力が変化したら、それに合わせて即座に変化してほしいですね。 割り込み処理を使わないため、この二つの処理は素直にmain関数内で処理することにします。さて、どういう動きになるでしょう。 【ダウンロード】 int_itu_test0.c 右クリックで「対象をファイルに保存」 【ダウンロード】 myfunc.h 右クリックで「対象をファイルに保存」
それでは、プログラムの中身について見てみましょう。 #include "iodefine.h"ではH8/3052Hのハードウェアレジスタを操作するための構造体変数、#include "myfunc.h"では、自作の汎用関数を、それぞれ利用できるようにします。今回はmyfunc.hの中に記述されている時間かせぎ関数を利用します。 main関数では、初めに変数の宣言や、H8/3052FのPIOポートの初期化を行います。今回はport1を出力、port2をプルアップ付きの入力、port3を出力に設定しています。その後、myfunc.hに記述されている時間かせぎ関数を利用するための初期化処理 itu0_wait_init( );を実行し、続いてport3から出力するLED表示パターンを0xAA; (●○●○ ●○●○)に初期化しています。 さて、次のwile(1){ }のループ処理が、今回のプログラムのメイン部分になります。はじめに、P1.DR.BYTE = P2.DR.BYTE;により、port2のBIT情報をそのままport1に出力させます。次に、port3に出力させるデータを決定する処理をswitch文で行っています。今回、port3への出力は0xAA(●○●○ ●○●○)と0x55(○●○● ○●○●)を交互に出力させます。この出力パターンを変数modeに格納しておき、P3.DR.BYTE = mode;でport3に出力しています。 最後に、port3の出力を1秒ごとに変化させるため、itu0_wait(1000);を実行していますが... ここでitu0_wait(1000);を実行すると、当然同じwhile(1)ループの中にあるP1.DR.BYTE = P2.DR.BYTE; も1秒ごとに実行されることになります。つまり、port2から入力されたBIT情報は即座にport1の出力に反映されず、port3の出力を変化させるための1秒ごとのタイミングにならないと反映されない、という動きになります。 それでは、とこか他の場所にitu0_wait(1000);を入れてみたらどうでしょう... しかし結果は同じことです。 itu0_wait(1000);で1秒を数えている間は、他のことは何もできないのです。 3.ITU割り込みプログラム それでは、次にHEW4開発環境の割り込みプログラムでの事例をご紹介します。今回のプログラムでも、先ほどと同じ二つの処理を行います。ひとつは、port2から入力されたBIT情報をそのままport1に出力させます。もうひとつは、port3の出力を1秒ごとに変化させます。今回はITU1のGRAがコンペアマッチした時に発生する割り込みを使って1秒間のインターバルを生成します。 今度はport3の出力を正確に1秒ごとに変化させつつ、port1の出力はport2の入力が変化したら、それに合わせて即座に変化させてみましょう。 プログラムで利用するITUの基本的な動作とレジスタの説明については「H8/3052F ITUで時間かせぎ」のページを参照して下さい。以下に、ITUを割り込みで利用するためのレジスタについてご紹介します。 ■TIER タイマインタラプトイネーブルレジスタ 各ITUチャンネルごとにあるレジスタで、以下の要因で発生する割り込みの許可を設定します。
OVF TSR タイマーステータスレジスタのOVFビットが1になったときに割り込みを発生させるかどうかを設定します。0なら発生させず、1なら発生させます。TSRのOVFビットは、TCNTタイマカウンタが0xFFFFを超えてオーバーフローしたときに1になります。 IMEB TSR タイマーステータスレジスタのIMEBビットが1になったときに割り込みを発生させるかどうかを設定します、0なら発生させず、1なら発生させます。TSRのIMEBビットは、TCNTがGRB ゼネラルレジスタBと同じ値になると1になります。 IMEA TSR タイマーステータスレジスタのIMEAビットが1になったときに割り込みを発生させるかどうかを設定します、0なら発生させず、1なら発生させます。TSRのIMEAビットは、TCNTがGRA ゼネラルレジスタAと同じ値になると1になります。 今回のダウンロードファイルは、HEW4のプロジェクトごとをZIPファイルに圧縮していますので、任意の場所に解凍してからHEW4の「ファイル」→「ワークスペースを開く」から拡張子.hwsのプロジェクトファイルを開いて利用してください。 【ダウンロード】 int_itu_test1.zip 右クリックで「対象をファイルに保存」
それではプログラムについて見てみましょう。 #include "iodefine.h" をインクルードしてH8/3052Fのハードウェアレジスタ構造体変数を使えるようにします。今回はmyfunc.hは利用しません。また、main関数と割り込み処理先で共通で利用する変数 cntとmodeの宣言をはじめにしておきます。 ■main関数 H8/3052FのPIOポートの初期化を行い、port1を出力、port2をプルアップ付きの入力、port3を出力に設定しています。また、割り込みを行わせるITU1の初期設定を行い、1mS(1/1000秒)でGRAコンペアマッチが発生するようにしています。また、ITU1.TIER.BYTE = 0xF9;でTIERのIMEAビットを1とし、GRAのコンペアマッチによる割り込みを許可しています。その後、while(1){ }ループに入る前に、変数cntとmodeを初期化します。 さて、while(1){ }ループの中身がmain関数でさせたい処理になりますが、P1.DR.BYTE = P2.DR.BYTE; しかありません。つまり、port2のBIT情報をport1に出力するだけで、port3の出力を変化させる処理は一切記述されていません。その部分は、次の割り込み処理部分に記述しています。 ■int_imia1( )関数 この関数が、ITU1のGRAがコンペアマッチしたときに割り込み処理によって実行される部分になります。先ずcnt変数の値を1だけ増やします。次のif文 if (cnt == 1000){ }でcntの値が1000かどうかを判定し、1000でなければITU1.TSR.BIT.IMFA = 0;を実行し、GRAコンペアマッチが発生したことを示すフラグをクリアして、割り込み処理を終了します。TCNTタイマカウンタは、GRAがコンペアマッチしたタイミングでクリアされて再び0から数え始め、次にGRAコンペアマッチしたタイミングで、再び割り込みが発生します。 もし、cnt変数が1000に達したら、if文の中が実行されます。先ずcnt変数を0にクリアし、port3に出力するためのデータであるmode変数の値を設定します。その後、P3.DR.BYTE = mode;により、port3の出力が変化します。つまり、1秒(1000mS)ごとにport3の出力が変化することになります。 ■割り込み関数を実行させるための処理 さて、先ほどのint_imia1( )関数ですが、上記のプログラム中ではどこからも呼ばれていません。いったいとこから呼ばれて実行されているのでしょうか。そのための仕掛けが、HEW4プロジェクトの中に含まれるintprg.cの中に隠されています。HEW4からintprg.cを開いてください。
このintprg.cは、HEW4でプロジェクトを作成した時に自動で生成されるソースです。H8/3052Fで扱うことができる全ての割り込み要因のベクタテーブルがあらかじめ記述されており、それぞれに /* sleep(); */ と記述されている通り、無処理扱いとしてあります。したがって、利用したい割り込み要因に対して、実行させたい割り込み関数を記述してあげることで、割り込み処理を実行させることが出来るようになります。 今回の事例では、ITU1のGRAがコンペアマッチしたときに発生する割り込みを利用したいので、IMIA1のベクタテーブル部分を利用します。割り込みが発生したときに実行させる割り込み関数の関数名は何でもかまいません。今回、intprg.cに手を加えた部分は、上記プログラムの青文字部分のみになります。 実は、このほかにもH8/3052Fマイコンの基本的な動作を決定しているCCR コントロールレジスタというものがあり、そこで割り込みマスクを行うなどの大本の設定があります。HEW4のプロジェクトでは、resetprg.cの中でその設定が記述されていますが、プロジェクトが生成された段階で割り込み許可となっているため、ここでは特に意識する必要はありません。 4.プログラムの実行 ■割り込まないプログラム int_itu_test0.c 【MOVIE】 ダブルクリックで再生されます。
■ITU割り込みプログラム int_itu_test1.c 【MOVIE】 ダブルクリックで再生されます。
今回は、割り込み処理の考え方とHEW4の環境下での割り込みプログラムの事例を見てみました。マイコンプログラムにおいて割り込み処理は非常に重要なもので、色々な場面で使われます。難しそうだということで敬遠しがちかもしれませんが、少しずつ慣れていくことも必要でしょう。ここでひとつ。「2.割り込まないプログラム」でご紹介したプログラムの事例ですが、while(1){ }ループの中でitu0_wait(1000);によって丸々1秒を待たず、割り込みプログラムの事例でご紹介した手法と同じくitu0_wait(1);で1ms(1/1000秒)ごとにwhile(1){ }ループを回せば、見た目にport1の出力はport2の変化に即座に対応して変化させることが可能です。それなら割り込みなど使わなくてもいいじゃんということになりますが、そんなことはありません。見た目に瞬時に対応していても、プログラムの処理としては1mSの待機時間が発生していることに変わりはありません。また、割り込みプログラムにより、main関数の記述は主な処理に専念した分かりやすいプログラムにすることができますし、main関数のwhile(1){ }ループ内に時間がかかる処理が含まれている場合、itu0_wait(1);が実行される間隔自体が長くなり、正確な1秒間隔を得ることが出来なくなりますね。割り込み要因には内部機能から呼ばれる場合や、周辺デバイスから呼ばれる場合など、様々あります。それらの割り込み要求に即座に答えて迅速に処理する、という必要性は理解して頂けると思います。 |