4

C の switch ステートメントを使用して、組み込みシステム用の単純なステート マシンを実装しました。ルックアップ テーブルで関数ポインターを使用した方がよいことはわかっていますが、それは次のステップとして取っておきます。

私のステート マシンには次の状態があります。

  1. 起動(初期状態)
  2. 起動エラー。
  3. アイドル (システムはこの状態での入力のみをチェックします。表示などは更新しません。単に「アイドル」です)。
  4. チェック(これが実際のアプリケーションです)
  5. プログラム
  6. 内部メモリにコピー

システムが起動すると、ポートを構成し、ディスプレイを初期化し、SPI バスに接続された IC とハンドシェイクして、すべてが A-OK であることを確認する起動状態に入ります。その場合、システムはアイドル状態に入ります。そうでない場合は、スタートアップ エラー状態に入り、LCD にエラーを表示し、変数にフラグを立ててから、アイドル状態に入ります。

アイドル状態では、プログラムはマイクロコントローラーの 3 つのピンをポーリングして、3 つのボタン (チェック、プログラム、メモリにコピー) のいずれかが押されているかどうかを確認します。押されたボタンに応じて、適切な状態になり、コードを実行し、LCD を更新してから、アイドル状態に戻ります。注: システムにハードウェア障害があった場合、システムはボタンが押されても気にしません。Startup Error 状態は、hardware_fault と呼ばれる変数にフラグを立てます。これが設定されている場合、Idle 状態が入力ボタンのポーリングに煩わ​​されないようにします。

ステート マシンを実装するのはこれが初めてで、これが適切な設計かどうか確信が持てませんでした。Idle 状態で入力をポーリングする FSM の例を実際に見たことがありません。代わりに、ほとんどの例は本質的にかなり連続しているようです (たとえば、カウンターのように)。それで、私の質問は、私のデザインは合理的ですか? それは機能しますが、ここにいる誰もが知っているように、悪いデザインと良いデザインがあります。

4

1 に答える 1

7

コードを switch ステートメントからリファクタリングし、言及したように遷移テーブルを使用する必要があります。スイッチの周りのコードはスケーリングされず、乱雑で、すぐに読み取りや更新が困難になります。

あなたの質問に答えると、ほとんどのステート マシンは実際にはあなたのようなものです。イベント (あなたの場合は投票) に反応します。それらが純粋にシーケンシャルで、イベントがまったくない場合、正規表現の実装など、特殊な用途向けです...

注意として、Idleステートはステート マシン ライブラリのランタイム コアと同等です。イベントをインターセプトし、ステート マシンにポストします。ステート マシン ライブラリを使用すると、クライアント コードから「隠蔽」されますが、あなたのような必要最小限の実装では、イベント ポーリングを明示的に行う必要があります。

そして今、設計の批評: グローバル変数は一般的に避けるべきであり、ステートマシンでは特にそうです。状態は、グローバル変数Idleについて認識すべきではありません。hardware_faultステート マシンでは、状態を追加することによって、ステート マシン自体にグローバル変数を「埋め込む」ように努める必要があります (意味がある場合)。(階層型) ステート マシンの非常に優れた紹介とその根拠の説明は、こちらにあります。

UML 表記 (チュートリアルについてはこちらを参照) を使用すると、ステート マシンは次のようになります。 ステートマシン v1

hardware_fault依存関係をまとめ て削除する簡単なリファクタリングは次のとおりです。ステートマシン v2

つまりStartupError、 に遷移することなく、その状態に永遠にとどまりますIdle

ε (イプシロン) は、空の遷移を表します。つまり、ソース ノードに関連付けられたアクティビティが終了するとすぐに、イベントなしで実行される遷移です。これが、「シーケンシャル」ステート マシンの意味です。

また、非常に簡単で強力なPlantUMLで生成された最初の図 (2 番目の図も非常に似ています) のソースを含めます。

@startuml

skinparam shadowing false
title Embedded v1

state Startup
state StartupError
state Idle
state Program
state Check
state Copy

Startup : entry/ init HW
StartupError: entry/\n  display error\n  hw_fault = true
Idle : do/ if not hw_fault: poll
Program : do/ program
Check : do/ check
Copy : do/ copy

[*] -> Startup

Startup -> StartupError : ε [error]
Startup --> Idle : ε [not error]

StartupError --> Idle : ε

Idle --> Program : program_btn
Idle --> Check : check_btn
Idle --> Copy : copy_btn

Program --> Idle : ε

Check --> Idle : ε

Copy --> Idle : ε

@enduml
于 2012-07-22T19:46:16.370 に答える