先週は、タイマー割込みの話のため、3つほどC言語の関数 フラグを利用して、複数のタイマーを利用しよう
を作成した哲さん。帰り道、どうやって、タイマー割込みに
結び付けるのかを考えていた。結局わからずに、今日も愛犬
ロボを連れて、啓二さんの仕事部屋にやってきます。
哲 :こんちは。先週の関数は、タイマー割込みとどう結び つくのか、わからないんだ。 啓二:あまり、難しいことじゃない。監督が、各関数を呼び 出す。各関数は、待ち状態のときに、タイマー割込み を使う。 哲 :でも、作った関数は、タイマー割込みを使ってないぞ。 啓二:そうさ。各関数から呼び出されるサブの関数を定義して いないから、まだ、見えないんだ。 哲 :えー、そうだったのか。悩んで、損したなあ。 啓二:じゃあ、1分を計測する処理を考えてみよう。 マイコンでは、1秒周期でタイマー割込み発生と仮定だ。 哲 :1分は、作れないのか。 啓二:作れなくはないけれど、ハードウエアの制約が多くなる から、1秒程度にしておく。 哲 :1秒周期の割込みなら、カウンタを使って、0から+1 していって、60になったら、それを知らせるのか。 啓二:ああ、そういう仕掛けで充分だ。 哲 :じゃあ、洗濯のときの、待ちを考えてみるか。 /* 洗濯方法と時間を設定し、ボタンを押す。 */ case 5 : set_manner( WASH ); set_time( M15 ); landry_state++ ; break ; /* 洗濯が終わるまで待つ。 */ case 6 : if ( time_up() == YES ) landry_state++ ; break ; となっているから、関数set_time( M15 );で15分を設定 したとすると、15×60=900をカウントすればいいか。 関数を呼び出したら、この値を計算しておけばいいんだな。 そして、0から+1ずつ加算して、一致したかどうか判定だ。 啓二:それでもいいけれど、900を計算して、タイマー割込みで 1ずつ減らしていく方がいい。 哲 :関数time_upで、0かどうか判定すれば、いいからか。 啓二:そういうこと。洗濯タスクだけが利用する、時間カウンタの landry_countを用意する。 哲 :わかったぞ。2つの関数は、次のように定義するといいんだ。 void set_time(int cnt) { landry_count = cnt * 60 ; } int time_up(void) { int result ; result = NO ; if ( landry_count == 0 ) result = YES ; return result ; } 啓二:ほほう。そう定義したか。じゃ、1秒周期のタイマー割込み では、どういうことをするんだ。 哲 :簡単、簡単。 void timer_handler(void) { if ( landry_count > 0 ) landry_count-- ; } これで、set_timeが値を正にすれば、タイマー割込みが 減算していってくれる。 呼出し側は、結果を待っていればいい。 啓二:論理上は、それでいいんだが、信頼性が問題になる。 割込みは、出来るだけ短時間で終了させたいんだ。 お前のやり方より、少しましな方法がある。フラグ利用だ。 哲 :フラグか。じゃあ、こういう風にするのか。 void set_time(int cnt) { landry_count = cnt * 60 ; landry_flag = ON ; } void timer_handler(void) { if ( landry_flag == ON ) { landry_count-- ; if ( landry_count == 0 ) landry_flag = OFF ; } } 啓二:2つの関数を、こんな具合にしたならば、残りの関数も 変えてみた方がいいかな。 哲 :じゃあ、こんな風に変えてみるか。 int time_up(void) { int result ; result = NO ; if ( landry_flag == OFF ) result = YES ; return result ; } フラグで、1バイトや2バイトを使うと無駄なような気が。 啓二:フラグは、1ビットでいいんだ。1バイトあれば、8個の フラグを使えるさ。 哲 :ああ、そうか。1バイトで8個のフラグを扱えば、メモリの 節約になるし、制御を考えるときに、1バイトに集約されて いるから、わかりやすくなるのか。 啓二:そういうことだ。 哲 :うん、うん。Windowsのプログラムよりも、柔軟性があるな。 自分のアイデアで、メモリが節約でき、より理解しやすい 方向に持っていける。 啓二:1バイトで8個のフラグを使えるということは、8つの呼出し 側を、1個のタイマー割込みで面倒をみれるということ。 だから、フラグを利用して、1個のタイマーが、フラグの数 だけあるように見える。 哲 :そういう結論になるよな。うん、これは、Windowsのプログラム でも使えそうだな。。 啓二:じゃあ、3つの関数を監督する関数を定義してみてくれ。 哲 :そうだ。それが、まだだった。 ええっと、変数を整理してみるか。 洗濯は、フラグが2つ。landry_start、landry_finish。 あれ、これだけで制御できちゃうのか。 啓二:そうみたいだな。監督関数を定義してみろよ。 哲 :よし、やってみるか。 void manager(void) { switch ( state ) { case 0 : /* 各タスク起動 */ landry_start = YES ; bath_start = YES ; cook_start = YES ; state++ ; break ; case 1 : /* 各タスク終了確認 */ if ( (landry_finish == YES) && (bath_finish == YES) && (cook_finish == YES) ) { state++ ; } case 2 : break ; } landry_task() ; bath_task() ; cook_task() ; } こんなもんか。 啓二:どれどれ、いいじゃないか。フラグを6つ利用しているな。 1バイトの変数を用意して、その上位と下位の各4ビット を、start、finishに割当ててしまえばいいな。 哲 :おい。 啓二:何だよ。 哲 :TSS(Time Sharing System)は、こういうメカニズムで 動かしているのかな。 啓二:おそらく、そうだと思う。TSSの場合は、動的にタスクを 増やしたり、減らしたりできるけれど、マイコンでは用途が 限定されているから、固定だけれどな。 哲 :固定だとしても、機能を追加するときは、その機能用タスク を定義して、起動は、監督関数に一任すればいいよな。 啓二:各処理の応答許容時間があるから、それさえクリアできれば 機能追加は簡単だな。それよりも、既存機能拡張のときに、 その機能だけに集中できることもメリットだな。 哲 :うひゃあ。今度、うちの新人を教育してくれよ。 啓二:自分で教育しろよ。OJTといって、仕事を任せてしまうのは 結構だが、コードレビューしたり、自分のノウハウを伝授して おかないと、お前が倒れたら、業務が停止して倒産だぞ。 それに、馬を川に連れて行くことはできても、水は飲ませられ ないぞ。そこを考えろよ。 哲 :うーん、あいつは、何に興味があるんだろうか。 啓二:ところで、お前が作った監督関数は、まだ未完だ。 哲 :えっ、何故だ。 啓二:各タスクが、今、どういう状態かを把握していないからさ。 何かの原因で、タスクが止まったり、同じことを繰返して しまうと、手も足もでない。監督は、そういうときに、次 に進めたり、そのタスクを消滅させないと駄目だ。 哲 :なるほど。確かに、起動と自動停止を確認しているだけだ。 啓二:ここからが、腕の見せ所なんだ。動かすだけなら、プロは絶対 にできる。しかし、アマとの違いは、障害があったときの復旧 が必要だってことさ。 哲 :そうだな。ロボットのアームが動いて、人の頭を殴ったなんて なると、大事だしなあ。 啓二:だろう。だから、そのあたりも考えて、マイコンのプログラム を考えようぜ。じゃあ、また、来週だ。