組み込みまするβ

組み込みソフトウェア初学者向けブログ

組み込みソフト技術者の新人研修

組み込みソフトの新人研修で実施する内容について、何をどのように教えていくのがよいのかを考えて記事にしました。 なお、C言語の理解が前提条件となりますので、理解が足りていない場合はC言語の書籍やC言語の副読本等で逐次補っていくとよいでしょう。

学習内容

目標としてはシングルタスクで小規模なプログラムを作れるようになること。 もちろん組み込みソフトウェアなので、時間待ちやタイムアウト、実時間制約や割り込みの理解も含めています。

一つはLEDを使った交通信号機の実現、 もう一つは組み込みシステムのデバッグ用途でよく使用される簡易シェルを作るのはいかがでしょうか? ここでいう簡易シェルとは、ホストPCとターゲットとなる組み込みシステムをシリアルケーブル(RS232やUSB)で接続して、 TeraTerm等のターミナルソフトと通信可能な簡易シェルのことです。

課題で作成するコードは後の開発で再利用するつもりで作成してもらいます。 作成当初はいろいろと考慮が足りなかったりすると思うので、 継続的なメンテナンス(バグ修正や改善)を行いつつ、課題に取り組んでもらうと良いのではないでしょうか。

対象となるハードウェア

実務に近い自社製品のハードウェアを用意できればよいのですが、 難しければArduinoやmbed等の小規模なハードウェアを採用しましょう。 但し、TimerやUART等の周辺回路のデータシートを読み解いてドライバを作成することも目的の一つなので、 当然ですがArduinoやmbedで提供されているドライバや便利なライブラリ等は使用禁止です。

  • 周辺回路(ほとんどの組み込みCPUにはTimer、UART、GPIOがあるので問題はないでしょう)
    • Timer
    • UART:RS232(レベル変換) または USB(仮想シリアル)
    • GPIO(PIO):LED(2個以上)

ハードウェア制御関連の課題

  • LEDドライバの作成
    • LEDチカチカ(点灯、消灯時間の実現方法を各自で考える)
  • Timerドライバの作成(割り込み無し版)
  • UARTドライバの作成
    • 送信処理(送信データレジスタにデータを書く)
    • 受信待ち(受信するまで待つ)
    • 受信の確認(受信したデータをそのまま送信する)
    • 関数インターフェイスを考える
    • 受信の監視(ポーリングと割り込み)
    • 送信可能の監視(ポーリングと割り込み)
    • バッファリングの意味と使い方を学ぶ(バッファが必要な理由を考える)

-- 余裕があれば以下も

  • Timerドライバの作成(割り込み有り版)
    • 割り込み時に呼ばれるコールバック関数の登録
    • Timer割り込み時に登録されたコールバック関数呼び出し
  • チック(Tick)コンポーネントの作成
    • チックのサービスを提供するための関数インターフェイスを考える
    • Timerの割り込みを利用してチックを作成する

組み込みソフトウェアの学習(ハードウェア制御関連の課題中に教える等)

設計の基礎学習

  • モジュラープログラミング(抽象化/凝集度)
  • レイヤーアーキテクチャ
  • サブシステム

交通信号機の開発

先に作成したLEDドライバ及び、フリーランカウンタまたは組み込みソフト版chronoを利用して交通信号機を実現します。 具体的には複数のLEDを青、黄、赤の信号に見立てて作成します。

  • LEDが3bitあるなら自動車用信号機
    • [青:5秒点灯]→[黄:1秒点灯]→[赤:5秒点灯]→戻る
  • LEDが2bitなら歩行者用信号機
    • [青:5秒点灯]→[青:1秒点滅(100~200msec間隔)]→[赤:5秒点灯]→戻る

シングルタスクで交通信号機を作成する

簡易シェルの開発

先に作成したUARTドライバ等を利用して簡易シェルを作成します。 C言語のポインタや文字/文字列操作(標準ライブラリ)を正しく理解していないと作成できないので、 C言語の復習にはちょうど良い課題になると思います。

仕様
  • 簡易シェル

    • 動作仕様
      • エコーバック有り
      • コマンド入力待ち時はプロンプトを表示する
      • 処理すべきコマンドが無い(CRまで受信してない状態)時はメインループに戻る
      • CR受信まで文字数制限を設けること(エラー表示、もしくは制限を超えた受信文字を破棄等)
    • コマンド仕様
      • readコマンド(IO読み出し)
        • パラメータ引数
          • アドレス
          • アクセス幅(8/16/32bit)
      • writeコマンド(IO書き込み)
        • パラメータ引数
          • アドレス
          • データ
          • アクセス幅(8/16/32bit)
      • dumpコマンド(メモリダンプ)
        • パラメータ引数
          • アドレス
          • バイト(またはワード)数
          • アクセス幅(8/16/32bit)
      • fillコマンド(メモリフィル)
        • パラメータ引数
          • アドレス
          • データ
          • バイト(またはワード)数
          • アクセス幅(8/16/32bit)
      • helpコマンド(全コマンドの一覧表示)
    • 余裕があれば
      • エスケープシーケンスを受信できるようにする(受信した内容を表示するのみ)
      • コマンドヒストリーの機能を追加する(エスケープシーケンスの受信機能を使用する)
  • ターミナルアプリケーション(TeraTerm等)

    • 端末の設定
      • 改行コードは送受信共にCR
      • ローカルエコーなし
段階的な作業

実現方法は様々なので例として挙げておきます。

  • 固定長文字列型(FixedString)の作成:コマンドライン操作を簡単にする

    • 関数インターフェイス
      • create、destroy
      • ctor、dtor
      • copy、insert、replace、erase、terminate、clear、at
      • lengthFromPos、length、empty、full
      • maxSize
      • cStr
  • ConsoleとShellへ機能分割

    • Console(入出力制御)
      • 入出力先をUARTにする(Consoleを抽象化し、SerialConsoleを具象として対応してもよい)
      • エコープログラム(受信した文字を返す)を作成する
      • BackSpaceに対応する
      • CRに対応する
      • 続く…
    • Shell(パース、コマンドのディスパッチ)
      • Command仕様を定める(実行とヘルプを関数インターフェイスとして持つ)
      • Commandの登録(静的・動的のどちらでも可)
      • コマンドライン文字列からargc、argvに成型する
      • 続く…
  • 続く…

まとめ

基礎とはいえ短期間に沢山の項目を理解することは難しいので、 研修後の実際の開発業務を通じて少しずつ理解していくとよいでしょう。 それまではある程度曖昧な理解のままで進んでいくことになりますが、 様々な項目の詳細を理解するには多くの時間を必要とするので焦る必要はありません。 興味を持った時や、より深い理解が必要なタイミングが詳細な知識を得るきっかけになると思います。

組み込みソフト技術以前にC言語の文法を理解していない場合はコンパイルも通りませんし、 技術的知識が伴わない間は質問内容がうまく伝わらずにつまずく人も多いと思います。

  • 実際にコードを記述する前に
    • 何が目的なのか、どのような方法を用いて実現するのか、よく考えて設計しましょう(設計ができていればコード化はすぐにできます)
  • Cコンパイラのエラーメッセージがわからない
    • C言語の文法への理解が不足していると思いますので、C言語の書籍をよく読んで理解を深めましょう
    • エラーが発生しているコードの特定ができなければコードをコメントアウトするなりして特定しましょう(括弧の対応関係のミス等)
    • 関数の使い方がわからなければ関数リファレンスをよく読んで確認しましょう
      • 正しい使い方もわかっていないのに意図通りに動くことはありません
    • 既存プログラムのbuild等でエラーがわからないなら、動かせるレベルに達していないので基礎から段階を踏んで学習しましょう
      • 基礎学習を飛ばして動かし始めても、様々なポイントでつまづいて結局は基礎学習に戻ることになります
  • プログラムが思った通りに動かない(プログラムは書いた通りに動きます)
    • デバッガの使い方を知らなければ調べましょう
    • デバッガでトレースして変数の値や分岐結果を確認してみましょう(ステップ実行で確認)
    • リアルタイム系のプログラムならprintf相当で変数の値や分岐結果を表示して確認しましょう(確認したい箇所にprintfを埋め込む)
  • 技術的な質問をする前に説明文を書いてみよう
    • 一度読めばわかるレベルの誤字脱字がないか
    • 相手に伝わる説明になっているか
    • 状況を説明する際に相手が知りえない情報を正確に伝えているか
    • 何が目的でどのような手段で実現しようと考えたのか
    • どのような問題が起きて何が原因だと思うのか(思い込みがないか)
    • どこまで正常動作して(しているように見えて)、どこから異常動作なのか
    • どうやって訓練するか
  • 課題について
    • 基礎が身についていれば本課題はそれほど難しくはありません
      • 難しいと感じる箇所が弱点なので、そこを集中的に学習して克服しましょう
    • 課題をこなすことで知識を得ることができますので自分の頭で考えて解答を導きましょう
    • どうしてもわからなければ他の人に質問して解答を得ても良いですが・・・
      • 教えを乞うわけですから礼節を欠くことの無いようにしましょう(相手が目の前にいるのと同様に)
      • 解答だけを教えてもらうのではなく、何が問題だったのか、どのように解答を導けばよいのか、同様の問題が発生した時に自分で解決できるようになったのかを考えましょう