先週、啓二さんから、タイマー/カウンタの説明を聞いた哲 制御の基本は、タイマー割込みだ
さん。それでは、利用方法や対象は何なのかと疑問を持って
愛犬ロボの引き紐を掴んでいた。いきなりロボが止まる。何
かなと見ると、向こうからアイヌ犬がやって来たのだ。紐を
引いているのは、二十代と思われる青年だった。一瞬、幻を
見たのかと思った。アイヌ犬は、啓二さんが飼っていたパル
そっくりだった。巻き尾、茶色の毛並み、立ち耳、黒い鼻と
蒙古班のある舌等、どれをとってもパルの姿だった。
哲 :こんちは。不思議なモノを見ちゃったぞ。 お前の愛犬パルが歩いていた。 啓二:そうか。 哲 :驚かないのか。 啓二:ああ、驚かないさ。今日は、27日。パルの命日なんだ。 魂で会いきて、その帰りに、お前に姿を見せたのかも。 哲 :脅かすなよ。 啓二:パルだけじゃない、パルの娘のジュンは、死んでから 3日ほどは、自分がつけていた鈴を鳴らして、自分の 存在を知らせていたんだ。家族全員が聞いたなあ。 哲 :ひえー、塩だ、塩。 啓二:今、Aiboを置いているから、焼き餅を焼いているかも。 哲 :のんきなことを言って。 啓二:帰るのか。 哲 :いや、やっていく。 啓二:それじゃ、やるか。マイコンで何かを制御するときの 基本は、タイマー割込みだ。スイッチのチャタリング 除去もステートマシンも、タイマー割込みを使うと 機械的にプログラムを作っていける。 哲 :本当に、タイマー割込みが基本なのか。 啓二:ワンチップのマイコンには、タイマーモジュールが内蔵 されて、タイマー割込みが用意されている。つくる側で 必要だから、タイマーモジュールを入れているんだ。 ニーズがないのに、無理して入れないさ。現実を見て これを使わない手はないだろう。 哲 :確かになあ。で、タイマー割込みを利用したときと、 そうじゃないときで、プログラムにどう影響するんだ。 啓二:Windowsのプログラムのように、イベント(事象)を待ち つつ、他の処理に専念できることかな。 哲 :イメージがわかないなあ。具体例で、説明してくれよ。 啓二:よし。じゃあ、身近な例で考えてみるか。 そうだなあ、洗濯、風呂焚き、料理を同時に処理する としよう。 哲 :お前の生活そのものじゃないか。まあ、自宅では似た ようなことをしているけれどさ。 啓二:1つ1つの処理は、単純なんだが、手順を決めよう。 その方が理解しやすいから。 洗濯は、次のシーケンスになる。 洗濯槽に水をはる。 適量になるまで待つ。 水を止める。 洗濯物を入れる。 洗剤を入れる。 洗濯方法と時間を設定し、ボタンを押す。 洗濯が終わるまで待つ。 排水開始。 排水待ち。 すすぎの時間を設定し、ボタンを押す。 すすぎが終わるまで待つ。 脱水槽に洗濯物を移す。 脱水の時間を設定し、ボタンを押す。 脱水終了待ち。 洗濯物を干す。 哲 :お前、まだ、2槽洗濯機を使っているのか。 啓二:うん、どうも全自動洗濯機は苦手でさ。 次、風呂焚きシーケンスだ。 浴槽に水をはる。 適量になるまで待つ。 水を止める。 ボイラーで加熱する。 適温になるまで待つ。 ボイラーを止める。 哲 :風呂焚きの方が、処理は少ないなあ。 啓二:料理は、カステラを焼くことにするか。 じゃあ、こんなシーケンスだ。 小麦粉200gを計量し、深皿に入れる。 砂糖200gを計量し、深皿に入れる。 鶏卵6個を、白身と黄身によりわける。 小麦粉200gを粉引きで、サラサラにする。 白身を泡立てる。 泡立った白身に黄身を入れて、かき混ぜる。 小麦粉を鶏卵の中に入れて、かき混ぜる。 砂糖を鶏卵の中に入れて、かき混ぜる。 バニラエッセンスを振る。 焼き型に流し込む。 オーブンに入れる。 焼き上がりを待つ。 哲 :この前食べたカステラのレシピは、これか。 後で、細かいレシピを書き出してくれよ。 啓二:わかった。 哲 :この3つの処理を、タイマー割込みを利用して 実現するのか。 啓二:そう。ただタイマー割込みを利用するだけじゃなく どの処理も失敗しないで、時間が最小限になるよう 工夫する。 哲 :最初は、どこから作っていくんだ。 啓二:まず、タイムシェアリングしないとならないから、 タイマー割込みの最小周期を決める。この3つの 処理ならば、1分単位でいいかな。 そして、3つの処理を監督するルーチンと空きが 出来たときに実行する処理を一つに絞る。 哲 :空きが出来たときの処理は、監督ルーチンじゃ駄目か。 啓二:監督ルーチンは、処理の進行状況を見ていて、実際は 仕事はしないんだ。だから、この場合なら、空き時間 で処理するのは、料理になる。 哲 :何故だ。何故、料理が空き時間を使って実現する 処理になるんだ。 啓二:洗濯と風呂焚きは、水をはるときに待ちが発生する。 タイマー割込みで、1分ごとに見ても、10秒も見て ないから、すぐに他の処理に遷移した方が効率がいい。 哲 :ああ、なるほど。効率をよくするためにも、ある処理が 待ちに入ったなら、他の処理を実行するんだ。で、いよ いよやることがなくなったときに、やることを決めると いうことか。 啓二:そうなるなあ。じゃあ、3つの処理をC言語のswitch文 を利用した、ステートマシンで記述していこうか。 そうだなあ、ステートマシンを処理ではなくて、タスク と呼ぶか。 洗濯 landry_task 風呂焚き bath_task 料理 cook_task としておいて、監督は、managerとでもしておくか。 哲 :監督managerを、タイマー割込みで起こすようにするのか。 今の段階では、単に各タスクの内容を定義するだけで、 いいんだな。 啓二:そうだよ。もう、階層構造で全体を形にしているから、 どこから作っても、いいんだ。 哲 :じゃあ、まずは、洗濯から定義してみるか。 監督からは、トリガーをもらうことにしてと。 switch ( landry_state ) { /* 洗濯槽に水をはる。 */ case 0 : if ( landry_start == YES ) { store_water( ON ); landry_state++ ; } break ; /* 適量になるまで待つ。 */ case 1 : if ( measure_water_height() == CM40 ) landry_state++ ; break ; /* 水を止める。 */ case 2 : store_water( OFF ); landry_state++ ; break ; /* 洗濯物を入れる。*/ case 3 : throw_goods(); landry_state++ ; break ; /* 洗剤を入れる。 */ case 4 : throw_soap( OFF ); landry_state++ ; break ; /* 洗濯方法と時間を設定し、ボタンを押す。 */ case 5 : set_manner( WASH ); set_time( M15 ); landry_state++ ; break ; /* 洗濯が終わるまで待つ。 */ case 6 : if ( time_up() == YES ) landry_state++ ; break ; /* 排水開始。*/ case 7 : drain_water(); landry_state++ ; break ; /* 排水終了待ち。*/ case 8 : if ( empty_water() == YES ) landry_state++ ; break ; /* すすぎの時間を設定し、ボタンを押す。*/ case 9 : set_manner( CLEAN ); set_time( M15 ); landry_state++ ; break ; /* すすぎが終わるまで待つ。 */ case 10 : if ( time_up() == YES ) landry_state++ ; break ; /* 脱水槽に洗濯物を移す。 */ case 11 : transfer_goods(); landry_state++ ; break ; /* 脱水の時間を設定し、ボタンを押す。 */ case 12 : rotate( BARREL ); landry_state++ ; break ; /* 脱水終了待ち。 */ case 13 : if ( barrel_stop() == YES ) landry_state++ ; break ; /* 洗濯物を干す。 */ case 14 : dry_goods(); landry_finish = YES ; break ; } 啓二:ステートマシンにすると、機械的にできるだろう。 哲 :そうだな。トップダウンで、詳細は、後で決めるから 楽なことは楽だ。 啓二:じゃあ、他の2つもやってみよう。 哲 :楽勝、楽勝。 まずは、風呂焚きだ。 監督からは、トリガーをもらうことにしてと。 switch ( bath_state ) { /* 浴槽に水をはる。 */ case 0 : if ( bath_start == YES ) { store_water( ON ); bath_state++ ; } break ; /* 適量になるまで待つ。 */ case 1 : if ( measure_water_height() == CM40 ) bath_state++ ; break ; /* 水を止める。 */ case 2 : store_water( OFF ); bath_state++ ; break ; /* ボイラーで加熱する。 */ case 3 : boil( ON ); bath_state++ ; break ; /* 適温になるまで待つ。 */ case 4 : if ( measure_temparature() == TC42 ) bath_state++ ; break ; /* ボイラーを止める。 */ case 5 : boil( OFF ); bath_finish = YES ; break ; } これで、いいだろう。次は、料理か。 switch ( cook_state ) { /* 小麦粉200gを計量し、深皿に入れる。 */ case 0 : if ( cook_start == YES ) { store_meel_to_dish( 200 , A ); cook_state++ ; } break ; /* 砂糖200gを計量し、深皿に入れる。 */ case 1 : store_sugar_to_dish( 200 , B ); cook_state++ ; break ; /* 鶏卵6個を、白身と黄身によりわける。 */ case 2 : for ( i = 0 ; i < 6 ; i++ ) divide_egg( C , D ); cook_state++ ; break ; /* 小麦粉200gを粉引きで、サラサラにする。 */ case 3 : dipping( A ); cook_state++ ; break ; /* 白身を泡立てる。 */ case 4 : whipping( C ); cook_state++ ; break ; /* 泡立った白身に黄身を入れて、かき混ぜる。 */ case 5 : whipping( D ); cook_state++ ; break ; /* 小麦粉を鶏卵の中に入れて、かき混ぜる。 */ case 6 : whipping( A ); cook_state++ ; break ; /* 砂糖を鶏卵の中に入れて、かき混ぜる。 */ case 7 : condence( B ); cook_state++ ; break ; /* バニラエッセンスを振る。 */ case 8 : throw( VANIRA_ESSENCE ); cook_state++ ; break ; /* 焼き型に流し込む。 */ case 9 : spread_over_to_bancket(); cook_state++ ; break ; /* オーブンに入れる。 */ case 10 : put_to_oven(); bake( ON ); cook_state++ ; break ; /* 焼き上がりを待つ。 */ case 11 : if ( bake_up() == YES ) { bake( OFF ); cook_state++ ; } break ; /* */ case 12 : cook_finish = YES ; break ; } 啓二:まあ、こんなところだな。続きは、来週だ。