フラグを利用して、複数のタイマーを利用しよう

先週は、タイマー割込みの話のため、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といって、仕事を任せてしまうのは
      結構だが、コードレビューしたり、自分のノウハウを伝授して
      おかないと、お前が倒れたら、業務が停止して倒産だぞ。
      それに、馬を川に連れて行くことはできても、水は飲ませられ
      ないぞ。そこを考えろよ。
哲  :うーん、あいつは、何に興味があるんだろうか。
啓二:ところで、お前が作った監督関数は、まだ未完だ。
哲  :えっ、何故だ。
啓二:各タスクが、今、どういう状態かを把握していないからさ。
      何かの原因で、タスクが止まったり、同じことを繰返して
      しまうと、手も足もでない。監督は、そういうときに、次
      に進めたり、そのタスクを消滅させないと駄目だ。
哲  :なるほど。確かに、起動と自動停止を確認しているだけだ。
啓二:ここからが、腕の見せ所なんだ。動かすだけなら、プロは絶対
      にできる。しかし、アマとの違いは、障害があったときの復旧
      が必要だってことさ。
哲  :そうだな。ロボットのアームが動いて、人の頭を殴ったなんて
      なると、大事だしなあ。
啓二:だろう。だから、そのあたりも考えて、マイコンのプログラム
      を考えようぜ。じゃあ、また、来週だ。

戻る