目次

ステートマシンでイルミネーション

 乱数でイルミネーションを作成したときに
 ステートマシンを使う方も考えてみました。



 ステートマシンは、マスターと複数サブの2種に分けて
 マスターは、サブのステートマシンにトリガーを与えて
 与えた仕事の終了フラグを待ち、次のサブを動かす方式
 に。

 状態遷移図は、以下。



 状態遷移図から、Arduinoの関数loop()の内部の
 記述は、次のように単純。

void loop()
{
  /* top */
  do_state_machine();
  /* sub */
  fnA();
  fnB();
  fnC();
  fnD();
  fnE();
  fnF();
}

 トップにあるステートマシンは、他のステートマシンを
 動かすことが仕事なので、次のようにイベントトリガー
 フラグと終了フラグを使って記述。

void do_state_machine()
{
  switch ( state ) {
    /* do fnA() */
    case 0 :
             state = 1 ;
             state_a = 0 ;
             a_flag = ON ;
             a_end = OFF ;
             break ;
    /* wait */
    case 1 :
             state = 1 ;
             if ( a_end == ON ) {
               a_end = OFF ;
               state = 2 ; 
             }
             break ;

    /* do fnB() */
    case 2 :
             state = 3 ;
             state_b = 0 ;
             b_flag = ON ;
             b_end = OFF ;
             break ;
    /* wait */
    case 3 :
             state = 3 ;
             if ( b_end == ON ) {
               b_end = OFF ;
               state = 4 ; 
             }
             break ;

    /* do fnC1() */
    case 4 :
              state = 5 ;
              state_c = 0 ;
              c_flag = ON ;
              c_end  = OFF ;
              break ;
    case 5 :
              state = 5 ;
              if ( c_end == ON ) {
                state = 6 ;
                c_end = OFF ;
              }
              break ;

    /* do fnD() */
    case 6 :
              state = 7 ;
              state_d = 0 ;
              d_flag = ON ;
              d_end  = OFF ;
              break ;
    case 7 :
              state = 7 ;
              if ( d_end == ON ) {
                state = 8 ;
                d_end = OFF ;
              }
              break ;

    /* do fnE() */
    case 8 :
              state = 9 ;
              state_e = 0 ;
              e_flag = ON ;
              e_end  = OFF ;
              break ;
    case 9 :
              state = 9 ;
              if ( e_end == ON ) {
                state = 10 ;
                e_end = OFF ;
              }
              break ;

    /* do fnC4() */
    case 10 :
              state = 11 ;
              put_pd4(ON) ;
              slast = millis() + MYD2000 ;
              break ;
    case 11 :
              state = 11 ;
              if ( millis() > slast ) { state = 12 ; }
              break ;
    case 12 :
              state = 13 ;
              put_pd4(OFF) ;
              slast = millis() + MYD500 ;
              break ;
    case 13 :
              state = 13 ;
              if ( millis() > slast ) { state = 14 ; }
              break ;

    /* do fnF() */
    case 14 :
              state = 15 ;
              state_f = 0 ;
              f_flag = ON ;
              f_end  = OFF ;
              break ;
    case 15 :
              state = 15 ;
              if ( f_end == ON ) {
                state = 16 ;
                f_end = OFF ;
              }
              break ;

    /* complete */
    case 16 :
              state = 17 ;
              put_pd6(OFF);
              break ;

    case 17 :
              state = 0 ;
              put_pd7(OFF);
              delay(3000);
              break ;

    default :
             break ;
  }
}

 他のサブステートマシンを動かすとき、該当
 ステートマシンの状態変数を0、開始フラグ
 をセット、終了フラグをクリア。

 サブステートマシンは、開始フラグ(イベント
 フラグ)を見て、起動するか、しないかを判断
 するだけでよく、起動されるまで、何もせずに
 眠っていればよくなります。

 サブステートマシンfnA()は、次のように記述。

byte state_a ;
byte a_flag ;
byte a_end ;
unsigned long alast ;

void fnA()
{
  switch ( state_a ) {
    /* wait trigger */
    case 0 : state_a = 0 ;
             if ( a_flag == ON ) {
               a_flag = OFF ;
               state_a = 1 ;
             }
             break ;
    /* set counter */
    case 1 : state_a = 2 ;
             put_pd6(ON);
             alast = millis() + MYD50 ;
             break ;
    /* delay */
    case 2 : state_a = 2 ;
             if ( millis() > alast ) { state_a = 3 ; }
             break ;
    /* return first state */
    case 3 : state_a = 0 ;
             put_pd7(ON);
             a_end = ON ;
             break ;
    /* others */
    default :
             break ;
  }
}

 時間待ちでは、ArduinoのAPI関数を使い
 担当するピンの状態を更新。

 規定時間の経過を、millis()を使って
 現在と経過後時刻を計算しておくのが
 ポイント。

 図で表現すると、ある時点でシステムタイマーの
 値を取得し、規定時間を加算後、システムタイマー
 の値が計算値に達するまで、監視する構成にする
 のが、ミソ。



 システムの動作シーケンスを用意しておき、その
 シーケンスを複数の関数に分割。

 分割した関数をステートマシンで構成すると、マスタ
 となるステートマシンは、サブのステートマシンの
 動作タイミングを決める仕事だけに専念。

 システム全体の動作をまとめると、以下。

/* pin assign */
#define PD7 7
#define PD6 6
#define PD5 5
#define PD4 4

#define OFF 0
#define ON  OFF+1

#define MYD20   200
#define MYD50   500
#define MYD80   800
#define MYD100  100
#define MYD500  500
#define MYD2000 2000

byte state ;
unsigned long slast ;

void set_logical_values()
{
  digitalWrite(PD4,OFF);
  digitalWrite(PD5,OFF);
  digitalWrite(PD6,OFF);
  digitalWrite(PD7,OFF);
}

void set_pin_directions()
{
  pinMode(PD4,OUTPUT);
  pinMode(PD5,OUTPUT);
  pinMode(PD6,OUTPUT);
  pinMode(PD7,OUTPUT);
}

void put_pd6(byte x)
{
  digitalWrite(PD6,x);
}

void put_pd7(byte x)
{
  digitalWrite(PD7,x);
}

void put_pd5(byte x)
{
  digitalWrite(PD5,x) ;
}

void put_pd4(byte x)
{
  digitalWrite(PD4,x);
}

byte state_a ;
byte a_flag ;
byte a_end ;
unsigned long alast ;

void fnA()
{
  switch ( state_a ) {
    /* wait trigger */
    case 0 : state_a = 0 ;
             if ( a_flag == ON ) {
               a_flag = OFF ;
               state_a = 1 ;
             }
             break ;
    /* set counter */
    case 1 : state_a = 2 ;
             put_pd6(ON);
             alast = millis() + MYD50 ;
             break ;
    /* delay */
    case 2 : state_a = 2 ;
             if ( millis() > alast ) { state_a = 3 ; }
             break ;
    /* return first state */
    case 3 : state_a = 0 ;
             put_pd7(ON);
             a_end = ON ;
             break ;
    /* others */
    default :
             break ;
  }
}

byte state_b ;
byte b_flag ;
byte b_end ;

void fnB()
{
  switch ( state_b ) {
    /* wait trigger */
    case 0 :
             state_b = 0 ;
             if ( b_flag == ON ) {
               b_flag = OFF ;
               state_b = 1 ;
             }
             break ;
    /* set counter */
    case 1 :
             state_b = 2 ;
             put_pd5(ON) ;
             alast = millis() + MYD20 ;
             break ;
    /* delay */
    case 2 :
             state_b = 2 ;
             if ( millis() > alast ) { state_b = 3 ; }
             break ;
    /* set counter */
    case 3 :
             state_b = 4 ;
             put_pd5(OFF) ;
             alast = millis() + MYD50 ;
             break ;
    /* delay */
    case 4 :
             state_b = 4 ;
             if ( millis() > alast ) { state_b = 5 ; }
             break ;
    /* return first state */
    case 5 :
             state_b = 0 ;
             b_end = ON ;
             break ;
    /* others */
    default :
             break ;
  }
}

byte state_c ;
byte c_flag ;
byte c_cnt ;
byte c_end ;
unsigned long clast ;

void fnC()
{
  switch ( state_c ) {
    /* wait trigger */
    case 0 : state_c = 0 ;
             if ( c_flag == ON ) {
               c_flag = OFF ;
               state_c = 1 ;
             }
             break ;
    /* set repeat counter */
    case 1 : 
             state_c = 2 ;
             c_cnt = 3 ;
             break ;
    /* repeat */
    case 2 :
             state_c = 3 ;
             if ( c_cnt == 0 ) { state_c = 8 ; }
             break ;
    /* enable and set counter */
    case 3 :
             state_c = 4 ;
             put_pd5(ON) ;
             clast = millis() + MYD100 ;
             break ;
    /* delay */
    case 4 :
             state_c = 4 ;
             if ( millis() > clast ) { state_c = 5 ; }
             break ;
    /* disble and set counter */
    case 5 :
             state_c = 6 ;
             put_pd5(OFF) ;
             clast = millis() + MYD500 ;
             break ;
    /* delay */
    case 6 :
             state_c = 6 ;
             if ( millis() > clast ) { state_c = 7 ; }
             break ;
    /* decrement */
    case 7 : 
             state_c = 2 ;
             c_cnt-- ;
             break ;
    /* return first state */
    case 8 :
             state_c = 0 ;
             c_end = ON ;
             break ;
    /* others */
    default :
             break ;
  }
}

byte state_d ;
byte d_flag ;
byte d_cnt ;
byte d_end ;

void fnD()
{
  switch ( state_d ) {
    /* wait trigger */
    case 0 : state_d = 0 ;
             if ( d_flag == ON ) {
               d_flag = OFF ;
               state_d = 1 ;
             }
             break ;
    /* set repeat counter */
    case 1 : 
             state_d = 2 ;
             d_cnt = 4 ;
             break ;
    /* repeat */
    case 2 :
             state_d = 4 ;
             if ( d_cnt == 0 ) { state_d = 8 ; }
             break ;
    /* enable and set counter */
    case 3 :
             state_d = 4 ;
             put_pd5(ON) ;
             clast = millis() + 150 + 30 * (4 - d_cnt) ;
             break ;
    /* delay */
    case 4 :
             state_d = 4 ;
             if ( millis() > clast ) { state_d = 5 ; }
             break ;
    /* disable and set counter */
    case 5 :
             state_d = 6 ;
             put_pd5(OFF) ;
             clast = millis() + MYD500 ;
             break ;
    /* delay */
    case 6 :
             state_d = 6 ;
             if ( millis() > clast ) { state_d = 7 ; }
             break ;
    /* decrement */
    case 7 : 
             state_d = 2 ;
             d_cnt-- ;
             break ;
    /* return first state */
    case 8 : 
             state_d = 0 ;
             d_end = ON ;
             break ;
    /* others */
    default :
             break ;
  }
}

byte state_e ;
byte e_flag ;
byte e_cnt ;
byte e_end ;

void fnE()
{
  switch ( state_e ) {
    /* wait trigger */
    case 0 : state_e = 0 ;
             if ( e_flag == ON ) {
               e_flag = OFF ;
               state_e = 1 ;
             }
             break ;
    /* set repeat counter */
    case 1 : 
             state_e = 2 ;
             e_cnt = 3 ;
             break ;
    /* repeat */
    case 2 :
             state_e = 3 ;
             if ( e_cnt == 0 ) { state_e = 8 ; }
             break ;
    /* enable and set counter */
    case 3 :
             state_e = 4 ;
             put_pd5(ON) ;
             put_pd4(ON) ;
             clast = millis() + 270 + 30 * (3 - e_cnt) ;
             break ;
    /* delay */
    case 4 :
             state_e = 4 ;
             if ( millis() > clast ) { state_e = 5 ; }
             break ;
    /* disable and set counter */
    case 5 :
             state_e = 6 ;
             put_pd5(OFF) ;
             put_pd4(OFF);
             clast = millis() + MYD500 ;
             break ;
    /* delay */
    case 6 :
             state_e = 6 ;
             if ( millis() > clast ) { state_e = 7 ; }
             break ;
    /* decrement */
    case 7 : 
             state_e = 2 ;
             e_cnt-- ;
             break ;
    /* return first state */
    case 8 :
             state_e = 0 ;
             e_end = ON ;
             break ;
    /* others */
    default :
             break ;
  }
}

byte state_f ;
byte f_flag ;
byte f_cnt ;
byte f_end ;

void fnF()
{
  switch ( state_f ) {
    /* wait trigger */
    case 0 : 
             state_f = 0 ;
             if ( f_flag == ON ) {
               f_flag = OFF ;
               state_f = 1 ; 
             }
             break ;
    /* set repeat counter */
    case 1 : 
             state_f = 2 ;
             f_cnt = 2 ;
             break ;
    /* repeat */
    case 2 :
             state_f = 3 ;
             if ( f_cnt == 0 ) {  state_f = 8  ; }
             break ;
    /* enable and set counter */
    case 3 :
             state_f = 4 ;
             put_pd4(ON) ;
             clast = millis() + MYD80 ;
             break ;
    /* delay */
    case 4 :
             state_f = 4 ;
             if ( millis() > clast ) { state_f = 5 ; }
             break ;
    /* disable and set counter */
    case 5 :
             state_f = 6 ;
             put_pd4(OFF) ;
             clast = millis() + MYD80 ;
             break ;
    /* delay */
    case 6 :
             state_f = 6 ;
             if ( millis() > clast ) { state_f = 7 ; }
             break ;
    /* decrement */
    case 7 : 
             state_f = 2 ;
             f_cnt-- ;
             break ;
    /* exit */
    case 8 :
             state_f = 9 ;
             f_end = ON ;
             break ;
    /* return first state */
    case 9 :
             state_f = 0 ;
             break ;
    /* others */
    default :
             break ;
  }
}

void init_state_machine()
{
  state = 0 ;
}

void do_state_machine()
{
  switch ( state ) {
    /* do fnA() */
    case 0 :
             state = 1 ;
             state_a = 0 ;
             a_flag = ON ;
             a_end = OFF ;
             break ;
    /* wait */
    case 1 :
             state = 1 ;
             if ( a_end == ON ) {
               a_end = OFF ;
               state = 2 ; 
             }
             break ;

    /* do fnB() */
    case 2 :
             state = 3 ;
             state_b = 0 ;
             b_flag = ON ;
             b_end = OFF ;
             break ;
    /* wait */
    case 3 :
             state = 3 ;
             if ( b_end == ON ) {
               b_end = OFF ;
               state = 4 ; 
             }
             break ;

    /* do fnC1() */
    case 4 :
              state = 5 ;
              state_c = 0 ;
              c_flag = ON ;
              c_end  = OFF ;
              break ;
    case 5 :
              state = 5 ;
              if ( c_end == ON ) {
                state = 6 ;
                c_end = OFF ;
              }
              break ;

    /* do fnD() */
    case 6 :
              state = 7 ;
              state_d = 0 ;
              d_flag = ON ;
              d_end  = OFF ;
              break ;
    case 7 :
              state = 7 ;
              if ( d_end == ON ) {
                state = 8 ;
                d_end = OFF ;
              }
              break ;

    /* do fnE() */
    case 8 :
              state = 9 ;
              state_e = 0 ;
              e_flag = ON ;
              e_end  = OFF ;
              break ;
    case 9 :
              state = 9 ;
              if ( e_end == ON ) {
                state = 10 ;
                e_end = OFF ;
              }
              break ;

    /* do fnC4() */
    case 10 :
              state = 11 ;
              put_pd4(ON) ;
              slast = millis() + MYD2000 ;
              break ;
    case 11 :
              state = 11 ;
              if ( millis() > slast ) { state = 12 ; }
              break ;
    case 12 :
              state = 13 ;
              put_pd4(OFF) ;
              slast = millis() + MYD500 ;
              break ;
    case 13 :
              state = 13 ;
              if ( millis() > slast ) { state = 14 ; }
              break ;

    /* do fnF() */
    case 14 :
              state = 15 ;
              state_f = 0 ;
              f_flag = ON ;
              f_end  = OFF ;
              break ;
    case 15 :
              state = 15 ;
              if ( f_end == ON ) {
                state = 16 ;
                f_end = OFF ;
              }
              break ;

    /* complete */
    case 16 :
              state = 17 ;
              put_pd6(OFF);
              break ;

    case 17 :
              state = 0 ;
              put_pd7(OFF);
              delay(3000);
              break ;

    default :
             break ;
  }
}

void setup()
{
  /* set logical values */
  set_logical_values();

  /* set pin directions */
  set_pin_directions();

  init_state_machine();
}

void loop()
{
  /* top */
  do_state_machine();
  /* sub */
  fnA();
  fnB();
  fnC();
  fnD();
  fnE();
  fnF();
}

 マスターのステートマシンは、動作を反復する
 のか、一度で終了するのかを、1行変更で対応
 できます。

    case 17 :
              state = 0 ;
              put_pd7(OFF);
              delay(3000);
              break ;
      ↓
    case 17 :
              state = 17 ;
              put_pd7(OFF);
              delay(3000);
              break ;

 仕事を終えたなら、その状態に常に留まるように
 記述すると、ダイナミックストップ状態で、内部
 では動作しているのに、外部からみると停止状態
 に見える処理で対応できます。

 状態遷移図でみると、次の違いがあります。



 図でみると矢印が先頭に戻るのか、どうかだけ。

 その状態に留まるか、先頭の状態に戻るかで、処理を
 ワンショットかループか選択できるのが、ステート
 マシンの特徴。


目次

inserted by FC2 system