佐倉です。日曜日はマイクロマウスの日です。
今回は前回言った、システムのスケルトンを簡単に作ってみようかなと思います。
が、その前に「制御」とおおざっぱに書いていますが、制御が何を指しているのか?という声が聞こえてきたので、軽く書いておきます。
ここで「制御」と書いているのは姿勢や動きの制御です。モータのトルク制御用の電流フィードバック等は含めていません。(ちなみに、マイクロマウスでは電流フィードバックを行なっている機体はあまりありません。オープンループで逆起電力の補償をするのが一般的です。センサを載せるスペースがあったらそのぶん削りたい人が多いからです。)
さて、まずはプログラムのエントリポイントはこんな感じがいいんじゃないかなという例を紹介してみます。
int main(void) { InitHardware(); // ハードウェアの初期化を行う StartTimers(); // タイマを起動する Application(); // アプリケーション部分を呼び出す while(1); return 0; }
こんな感じがよさそうです。 さて、次は制御ループです。
void ExampleInterruptHandler(void) { ..... ..... ControlLoop(); // タイマに同期させて制御を呼び出す } void ControlLoop(void) { GetSensorData(); // 今回分のセンサのデータを取り出す Filter(); // センサのデータを加工し、必要な形に加工する GetMotionData(); // 動作のデータを読み込み、必要な形に加工する Control(); // 制御を行う Log(); // (ログを書き込む) }
このあたりはこんな感じになると思います。スケルトンだけにしてしまうと、すっきりして話も見えてきたのではないかなと思います。
さて、非同期部分とのやりとりは次回にまわして、低レベルでのプログラミング特有の悩みについて自分なりのやり方を紹介してみます。
この辺りのコードを書いていると悩むと思うのですが、センサやモータを扱うコードを書く時に、オブジェクト指向的な設計にするのか?という悩みです。
C言語でそれっぽくやろうとすると、クラスの代わりに構造体を使って、その構造体に関連する処理をする関数は構造体のポインタを受け取って処理をする…というような感じになると思います。テストやシミュレーションに使うには便利です。が、しかし、現実問題としてハードウェアはシングルトンなので、書いているとばかばかしくなってきますし、関数の引数にしつこく構造体へのポインタが入って残念な感じです。ただでさえ確保しにくいスタック領域もガリガリ削られてしまいます。
私は、使い回しよりはカプセル化を優先したほうが綺麗に見えるしバグも潰しやすいので、ソースコード1こ1こをクラスのような感じでコンパクトにしてその中にベタ書きしています。その中でのやりとりには、タブー視される事の多いグローバル変数もふんだんに使っています(もちろんexternは禁止)。ソースコード1つ1つがシングルトンのクラスだと捉えれば、グローバル変数もprivate変数のようなもので、気負いなく、汚いC言語にならず、楽しくプログラミングできるのでおすすめです。
次回は制御と非同期の部分について、スケルトンコードを交えながら考えていこうと思います。
【お知らせ】
初心者向けマイクロマウス合宿で佐倉が電気回路の講習をします。お楽しみに!
【佐倉の書棚からお勧め書籍】
【amazon バナー】