マイコン入門 ライントレースカーシステムの構築

マイコン
この記事は約7分で読めます。

こんちは、ナナです。

マイコン入門編も終わりに差し掛かってきました。最後の仕上げとなるシステムを統括するMAINモジュールの構築が記事の内容です。

本記事では次の疑問点を解消する内容となっています。

本記事で学習できること
  • システムを統括するモジュールの役割とは?
  • アプリケーションを構築するための、プログラム構築方法とは?

では、ライントレースカーを完成させましょう。

スポンサーリンク

ライントレースカーシステムにおけるMAINモジュールの役割

本章までで必要となるデバイスドライバのモジュール達が完成しました。

ここまで作成したモジュール

ここまでMAINモジュールはデバイスドライバモジュールの動作を検査するために利用してきましたが、これは本来の役割ではありません。

MAINモジュールの本来の役割は本システム全体の指揮を執るアプリケーションとして動作することです。目的のシステムを動かすために部下となるデバイスドライバに指示を出しながらミッションを遂行するのです。

では今回のライントレースカーシステムの動作仕様から見ていきましょう。

スポンサーリンク

ライントレースカーの動作仕様

ライントレースカーに対する動作仕様を決定します。

動作仕様項目

  • 電源を入れるとオレンジとグリーンLEDを点灯状態とする。
  • 電源投入直後はモーター動作は停止とする。
  • スイッチをユーザーが押すことでスピーカーから1秒間隔で「ド・レ・ミ」の3音を順番に出力する。
  • 「ド・レ・ミ」の出力が終わり次第、P制御による走行を開始する。
  • 走行中は1秒間隔で両LEDを点灯/消灯を繰り返す。
  • 走行中の光センサ値はシリアル通信で常にパソコンに送信し続ける。出力書式は「SEN:%d\n」とする。
  • 電源を切るまでは走り続ける。
スポンサーリンク

課題:ライントレースカーシステムを管理するMAINモジュールを作ろう

課題1

課題内容

MAINモジュールから各デバイスドライバモジュールを呼び出し、目的のライントレースカーシステムを構築せよ。

main.c

//------------------------------------------------
//  概  要:エントリーポイント
//------------------------------------------------
void main(void)
{
    //  システム初期化処理
    main_init();

    while(1)
    {

    }
}

課題が完成したらビルドを行いビュートローバー上で動作させ、期待動作通りに動くことを確認せよ。


期待動作

動作仕様項目の従い動作することを確認する。

起動時の実施すべき処理をシーケンス図で表現すると次のとおりである。この図を参考に起動時処理を行うとよい。

起動処理シーケンス

起動時シーケンス図
シーケンス図とは

統一モデリング言語(UML)の中の1つの記法である。UMLではソフトウェアのシステム構成を人が理解できる図として表記するためのルールを規定している。

シーケンス図はモジュール分割手法と非常に相性が良く、時系列でモジュール間の動作の流れを可視化することができる。詳細は「UML シーケンス図」で検索するとよい。

上記図内で利用しているブロックの意味は次のとおりである。

  • loop:反復処理を表す。()内にはループ回数を表現する。
  • opt:単発の条件式を示す。C言語におけるif文に相当する。
  • break:反復処理からの抜けることを示す。C言語におけるbreak文に相当する。
  • ref:別のシーケンス図を参照してほしいことを示す。

走行処理シーケンス

走行処理シーケンス図

main.c

//------------------------------------------------
//  概  要:エントリーポイント
//------------------------------------------------
void main(void)
{
    int             ledFlg = 0;     //  0:消灯 1:点灯
    unsigned long   time;           //  LED点滅用の時刻管理

    //  システム初期化処理
    main_init();

    //  起動時LED点灯処理
    Led_setLight(D_LED_KIND_ORANGE, D_LED_LIGHT_ON);
    Led_setLight(D_LED_KIND_GREEN, D_LED_LIGHT_ON);

    //  走行前準備処理
    while (1)
    {
        if (Sw_getPressed() == D_SW_PRESSED_ON)
        {
            int scale;

            //  ド・レ・ミの3音を1秒毎に出力
            for (scale = E_SPK_SCALE_DO ; scale <= E_SPK_SCALE_MI ; scale++)
            {
                Spk_start(scale);
                Timer_waitTime(1000);
            }

            Spk_stop();
            break;
        }
    }

    //  時刻初期化
    time = Timer_getTime();

    //  走行処理
    while (1)
    {
        unsigned short  sen1;
        unsigned short  sen2;
    
        //  P制御走行開始
        Lsen_getSensor(&sen1, &sen2);
        Motor_runPcon(sen1);

        //  シリアルへセンサ値送信
        sprintf(gMainSciString, "SEN:%d\n", sen1);
        Sci_putString(gMainSciString);

        //  1秒毎のLED点滅制御
        if (time + 1000 < Timer_getTime())
        {
            if (ledFlg == 0)
            {
                Led_setLight(D_LED_KIND_ORANGE, D_LED_LIGHT_ON);
                Led_setLight(D_LED_KIND_GREEN, D_LED_LIGHT_ON);
                ledFlg = 1;
            }
            else
            {
                Led_setLight(D_LED_KIND_ORANGE, D_LED_LIGHT_OFF);
                Led_setLight(D_LED_KIND_GREEN, D_LED_LIGHT_OFF);
                ledFlg = 0;
            }

            //  時刻更新
            time = Timer_getTime();
        }
    }
}

全体で70行程のプログラムであるが、各デバイスドライバのインターフェースを適宜呼び出すことで割と簡単に作ることができる。

スポンサーリンク

Q&A:アプリケーション作成においてよくある質問

本章にヒントとして出てくるシーケンス図というものは覚える必要があるのでしょうか?UMLというものについても学んだ方がよいのでしょうか?

はい。ソフトウェア開発を仕事にする方は特に学んだ方がよいでしょう。

UMLにはシーケンス図以外にもたくさんの図が規定されています。一度にすべてを覚えて使いこなすのは難しため、まずはよく使う図から覚えていくとよいです。シーケンス図はモジュール分割を行うシステムでは非常によく使われます。

上から下に時間が流れ、左から右へインターフェースの呼び出しが流れるように書きます。