田舎の組み込みプログラマーがわざわざ趣味でも色々開発してみようとあがく様を綴るブログです。
DMM.makeのクリエイターズマーケットに出品しています。

状態遷移指向プログラミング2017年05月31日

突然ですが

みなさん、状態遷移図は描いていますか?
そしてどんな風に実装していますか?
状態遷移とソースコードが違いすぎて悩んでいませんか?

当方も長年あれこれ試行錯誤してきたんですが、ようやく自分の求める形が見えてきましたのでちょっとお披露目してみます。

題して、状態遷移指向アクション駆動モジュールです。

勝手に改造

状態遷移図はこんな感じにアレンジしています。

基本の状態遷移

件のモジュールをパート(PART)と呼ぶことにします。
パート内部には状態遷移処理が仕込んであり、アクションを指示することで動作します。

ステート(STATE)は複数のコンディション(CONDITION)を持つことができ、パート→アクティブステート→アクティブコンディションの 順にアクションを評価します。
対応するアクションがなければ何もしません。

ステートには入退場処理機能も持たせてあり、特に退場処理ができることで色々捗ります。

こんな感じで機能毎にパートを用意し、あとはサイクリック・エグゼクティブ処理で回していくのが基本形です。
これで解決できない問題は後々作り込んでいくということで。

そして、お約束の

Lチカをやってみます。まずは状態遷移図から。

Lチカ状態遷移

ご覧のようにLEDのオン/オフと点滅ができる仕様です。
諸事情によりアクションは固定となっていますので、_BLINKなんて限定的なアクションを入れる余裕はなく、点滅は _UNIQUE(固有機能)アクションで実行するようにしています。

_TIMERアクション処理はジャンクションで書いてありますが、事情により合流ができませんので厳密にはジャンクションとは言えません。
チョイスについてはちゃんと実装する予定です。

では、いよいよソースコードに行きます。

;==========================================================
PART$ LED_CONTROL
;----------------------------------------------------------
blink_timer:    .blkw    1
blink_limit:    .blkw    1
;==========================================================
    ; コンディション定義
    CONDITION$_DEFINE C_LED_OFF        ;[消灯中]
    CONDITION$_DEFINE C_LED_ON         ;[点灯中]
    ; ステート定義
    STATE$_DEFINE LED_STATIC_OFF       ; 消灯<[消灯中]
    STATE$_DEFINE LED_STATIC_ON        ; 点灯<[点灯中]
    STATE$_DEFINE LED_BLINK_OFF        ; 点滅(OFF)<[消灯中]
    STATE$_DEFINE LED_BLINK_ON         ; 点滅(ON)<[点灯中]

    ;=======================================
    ;[消灯中]
    ;=======================================
    CONDITION$ C_LED_OFF
    _ON
        TRANS LED_STATIC_ON
    _UNIQUE
        TRANS LED_BLINK_ON
    _END

    ;=======================================
    ;[点灯中]
    ;=======================================
    CONDITION$ C_LED_ON
    _OFF
        TRANS LED_STATIC_OFF
    _UNIQUE
        TRANS LED_BLINK_OFF
    _END

    ;---------------------------------------
    ; 消灯<[消灯中]
    ;---------------------------------------
    STATE$ LED_STATIC_OFF, C_LED_OFF
        [ LED_PORT_BIT ] = OFF
    _END

    ;---------------------------------------
    ; 点灯<[点灯中]
    ;---------------------------------------
    STATE$ LED_STATIC_ON, C_LED_ON
        [ LED_PORT_BIT ] = ON
    _END

    ;---------------------------------------
    ; 点滅(OFF)<[消灯中]
    ;---------------------------------------
    STATE$ LED_BLINK_OFF, C_LED_OFF
        [ LED_PORT_BIT ] = OFF
        [ blink_timer ].w = 0
    _OFF
        TRANS LED_STATIC_OFF
    _TIMER
        [ blink_timer ].w = [ blink_timer ].w + [ PART_PARAM ].w
        if [ blink_timer ].w >= [ blink_limit ].w
            TRANS LED_BLINK_ON
        endif
    _END

    ;---------------------------------------
    ; 点滅(ON)<[点灯中]
    ;---------------------------------------
    STATE$ LED_BLINK_ON, C_LED_ON
        [ LED_PORT_BIT ] = ON
        [ blink_timer ].w = 0
    _ON
        TRANS LED_STATIC_ON
    _TIMER
        [ blink_timer ].w = [ blink_timer ].w + [ PART_PARAM ].w
        if [ blink_timer ].w >= [ blink_limit ].w
            TRANS LED_BLINK_OFF
        endif
    _END

;==========================================================
_INIT
    [ blink_limit ].w = 100
_SET
    [ blink_limit ].w = [ PART_PARAM ].w
;----------------------------------------------------------
_END
;==========================================================

状態遷移図からかなり忠実に落とし込めていると思うのですが、どうでしょう?
そしてこれは実際に動くコードなのです。

こういうことができるのも構造化アセンブラの素晴らしいところだと、忘れずにヨイショしておきます。

さて、このソースでは変数[blink_limit]の初期値を100としていて、単位はmsecのつもりです。
この値は_SETアクションで変更することができ、_TIMERアクションでは呼び出し周期をパラメータとします。

つまり、10msec周期で_TIMERアクションを呼び出すならパラメータを10にするということです。
そうすることで、点滅時のLEDのオン時間とオフ時間がそれぞれ100msecとなる仕掛けです。

我ながら

独自の道を突き進んでる感がハンパない気がしますが、趣味ですのでこれでいいんです。
まぁ、仕事の方でも似たようなことをしてるんですけどね。

それでは今回はこの辺で。