はじめに
この記事はマイクロマウス Advent Calendarの2日目です。
前回の記事はコヒロさんのアドベントカレンダーが寂しい件についてでした。
Advent Calendarの季節になると一年の終わりを感じます。いろんな人が書いた記事を眺めるのは楽しいので記事が埋まるといいなと思っています。
テーマ
マイクロマウスのソフトをどのように書くかについて、マイクロマウスの新作を作るたびに一から書き直すというプロセスを何度か経て現状しっくりきている形を紹介します。一つの記事ですべてを記述すると大変長くなってしまうので、以下のように何回かに分けて記述していきます。
- ソフトの階層構造 <- 今回はこの話
- アプリケーションの基本骨格
- アプリケーション実行単位(Module)
- アプリケーション実行単位(Activity)
- マイクロマウスのModule、Activity構成の一例
目標
ソフトを書くに当たって以下を目標としています。
- マイコンの変更に強く移植性の高いソフト構造を実現したい
- テストを記述しやすい構造としたい
- 同一コードをマイコンでもPC上でも実行可能としたい
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
ソフトの階層構造
ロボコン等で使われるソフトを何年にもわたって使い続けると熟成が進み、新しい機能をバグなしに追加することが難しくなってきます。そのため、ソフトの健全性を保つためにソフトの構造に気を配りつつコードを書くことが重要です。ここではソフトの構造を整理するためのはじめの一歩として以下の3レイヤーの導入を提案します。
PDL(Peripheral Driver Layer)
マイコンのAD変換やPWM出力といったペリフェラルを直接操作する関数群を記述するレイヤーです。
HAL(Hardware Abstraction Layer)
アプリケーションを動かすために必要なペリフェラル操作を呼び出すための関数群を記述するレイヤーです。HALの関数はPDLで定義した関数をラップした形になることがほとんどです。
AL(Application Layer)
ロボットのアプリケーションとしての処理を記述するレイヤーです。AL内でマイコンのペリフェラルを操作したいときは必ずHALで規定した関数のみを呼び出すようにします。
レイヤ分けの具体例
マイクロマウスの壁センサ関連の処理を記述するときにPDL、HAL、ALが具体的にどのようになるかを考えてみます。
壁センサはLEDとフォトトランジスタのペアから構成されるものとします。LEDはP22というピンにつながっていて、フォトトランジスタの電流検出抵抗はAN000というポートに割り当たっているとします。
壁センサを動作させるには以下の機能を実装する必要があります。
- マイコンのGPIOポートでLEDをON/OFFする
- LEDの光を壁に当てて反射した光をフォトトランジスタで受光し、電流検出抵抗によって電圧値に変換したものをAD値として読み取る
PDL
必要なペリフェラルはGPIOとADなので、それぞれ初期化と操作用関数を用意します。
HAL
PDLで定義した関数をラップするための関数を用意します。
AL
HALで用意した関数のみを用いて壁センサの処理を記述します。
このようなレイヤ分けをすることで、ソフトの移植を行うときにいじらなくてはならない範囲をPDLとHALのみに押し込めることができました。
マイコンとPCで同一コードを動かすための工夫
先ほど、壁センサを動作させるコードをどのようにレイヤ分けするかについて述べたので、引き続き説明にこの例を用いることとします。
マイクロマウスは最終的にロボット実機でプログラムを実行することになりますが、ソースコードの検証やテストをPC上でも行えるようにしておくと開発がと楽になります。
しかし、マイコンのペリフェラル操作のような記述がソースコードに混じっていると、PC上ではビルドを通すことはできません。
そのため、HALの関数内では以下のようなコンパイルスイッチを入れておくことをお勧めします。このコンパイルスイッチによりSILSという文字列がdefineされている場合はPDLで記述した処理を除外することが実現されています。
マイクロマウスのPDとHALの規模
実際にマイクロマウス規模のソフトでPDとHALがどれくらいの規模になるかの一例を示します。過去の記録をみると筆者のマイクロマウスの場合、関数の数でいうとHALだけで97個あったみたいです。
HALを設けずにソフトを書いていた場合にソフトの移植を行おうとすると、ソースコード全体に散らばった97個の関数をひたすら書き換える作業が発生することになります。
しかも、せっかく動作確認が取れていたアプリケーションに関する記述がされたファイルに手を入れることになってしまいます。
HALをいちいち書くのはめんどくさいと思われるかもしれませんが、ソフトの移植性を高め未来の工数を減らすためには有効です。
マイコンのペリフェラル部分の操作用関数の実装が終わった。今度はこれをラッピングするハードウェア抽象化レイヤーを作るぞ。完全に好みの問題になると思うんだけど、マイコンのペリフェラル毎にクラスを作るのはあまり好きじゃない。なので、マイコンのペリフェラル操作は簡単な関数にまとめてる。 pic.twitter.com/Ds7JDjVBwK
— R.Ganon (@qtfdl94q) March 15, 2020
ハードウェア抽象化レイヤーの関数は97個になった。内訳はAD x 5 , 不揮発性メモリ操作, GPIO x 8 , 位相計数 x 2, PWM x 4, SPI x 2, wait関数2系統, タイマ割り込み2系統, UART 2系統, ウォッチドッグ x 1。マイクロマウスを動かすのに必要な最小構成 + α くらいを用意。 pic.twitter.com/vwDfLSP7iy
— R.Ganon (@qtfdl94q) March 15, 2020
おわりに
マイクロマウスのソフトをどのように書くかについて、初回としてソフトの階層構造について述べました。次回はアプリケーションの基本骨格について書く予定です。
この記事執筆時点で次のAdvent Calendar記事が未登録状態です。誰か書いてくれると嬉しいです。
[…] ソフトの階層構造 <- 2021年のAdvent Calendar […]