目次
前
次
ステートマシンでイルミネーション
乱数でイルミネーションを作成したときに
ステートマシンを使う方も考えてみました。
ステートマシンは、マスターと複数サブの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 ;
仕事を終えたなら、その状態に常に留まるように
記述すると、ダイナミックストップ状態で、内部
では動作しているのに、外部からみると停止状態
に見える処理で対応できます。
状態遷移図でみると、次の違いがあります。
図でみると矢印が先頭に戻るのか、どうかだけ。
その状態に留まるか、先頭の状態に戻るかで、処理を
ワンショットかループか選択できるのが、ステート
マシンの特徴。
目次
前
次