制御の基本は、タイマー割込みだ

先週、啓二さんから、タイマー/カウンタの説明を聞いた哲
さん。それでは、利用方法や対象は何なのかと疑問を持って
愛犬ロボの引き紐を掴んでいた。いきなりロボが止まる。何
かなと見ると、向こうからアイヌ犬がやって来たのだ。紐を
引いているのは、二十代と思われる青年だった。一瞬、幻を
見たのかと思った。アイヌ犬は、啓二さんが飼っていたパル
そっくりだった。巻き尾、茶色の毛並み、立ち耳、黒い鼻と
蒙古班のある舌等、どれをとってもパルの姿だった。
哲  :こんちは。不思議なモノを見ちゃったぞ。
      お前の愛犬パルが歩いていた。
啓二:そうか。
哲  :驚かないのか。
啓二:ああ、驚かないさ。今日は、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 ;
        }
啓二:まあ、こんなところだな。続きは、来週だ。

戻る