mbed Advent Calendar 2015 の1日目の記事です。
注: この記事は technology preview 版のソフトウェアを利用したものになるので、今後変わる可能性があります。
mbed OSで新たに採用されたMINARというスケジューラーを触ってみたので、その挙動をまとめます。
mbed OSはRTOSではなく、Node.jsのようなイベント駆動のアーキテクチャになっています。
なので、スケジューラー周りはNode.jsを触ったことがある人だと理解しやすいかもしれません。
0. 準備
前回の記事に沿ってLチカが動くところまで行います。
1. app_start()
従来のmbedのプログラムではmain関数から処理が始まり、だいたい以下のようなコードを書いていました。
int main(){ do_initialize_something(); while (true) { do_loop_something(); } }
mbed OSでは main
を書かずに app_start
関数を書く必要があります。
void app_start(int, char**) { minar::Scheduler::postCallback(blinky).period(minar::milliseconds(500)); }
mbed OSのMINARは何を行っているかというと、下の擬似コードのようなことを行っています。
while (true) { while (event_available_to_schedule()) { // イベントが発火しているか schedule_event(); // 発火しているイベントの実行 ex. blinky() } sleep(); }
要は実行できるイベントがあれば、実行し、あとは寝ています。
2. 定期的なスケジューリング
1秒おきにLEDをチカチカさせるイベントを定期的に実行する場合、mbed OSのMINARを省略なしに使ったコードは以下のようになります。
#include "mbed-drivers/mbed.h" using mbed::util::FunctionPointer0; using mbed::util::FunctionPointerBind; using mbed::util::Event; static DigitalOut led(LED1); static void blinky(void) { led = !led; } void app_start(int, char**) { FunctionPointer0<void> ptr_to_blinky(blinky); // 関数へのポインターの生成 FunctionPointerBind<void> bind_of_blinky(ptr_to_blinky.bind()); // 引数をbind Event e(bind_of_blinky); // イベントを生成 minar::Scheduler::postCallback(e).period(minar::milliseconds(1000)); // 1秒ごとに実行 }
ポインターを生成してから、引数を固定して、その後にイベントを生成して、定期実行のキューを積むような処理になります。
FunctionPointer0
には FunctionPointer1
FunctionPointer2
FunctionPointer3
という種類があり、それぞれ引数が1個、2個、3個の関数に対応しています。
例えば引数が1つの関数を使ったコードは以下のようになります。
#include "mbed-drivers/mbed.h" using mbed::util::FunctionPointer1; using mbed::util::FunctionPointerBind; using mbed::util::Event; static DigitalOut led(LED1, 1); static void blinky(int i) { led = i; // 1 => off, 0 => on } void app_start(int, char**) { FunctionPointer1<void, int> ptr_to_blinky(blinky); // 関数へのポインターの生成 FunctionPointerBind<void> bind_of_blinky_on(ptr_to_blinky.bind(0)); // 引数(0)をbind FunctionPointerBind<void> bind_of_blinky_off(ptr_to_blinky.bind(1)); // 引数(1)をbind Event e_on(bind_of_blinky_on); // LEDを光らせるイベントを生成 Event e_off(bind_of_blinky_off); // LEDを消すイベントを生成 minar::Scheduler::postCallback(e_on).period(minar::milliseconds(2000)); // 2秒ごとに実行 minar::Scheduler::postCallback(e_off).delay(minar::milliseconds(1000)).period(minar::milliseconds(2000)); // 1秒後から2秒ごとに実行 }
先ほどのコードで FunctionPointer0
だったところが FunctionPointer1
になり、引数をbind時に渡せるようになりました。
このコードでは、引数が1のLED消すように設定したイベントと、引数が0のLEDを付けるイベントを交互に実行することで、1秒ごとのLチカを実現しています。
交互にイベントを実行したい場合、最初に発火するイベントのタイミングをずらしてあげないと、付いている時間が短すぎたり長すぎたりすることになります。
そこで postCallback(e_off)
のあとに delay
というメソッドを実行し、実行するタイミングを後ろにズラすことで、付く・消すという動作が交互に実行されるようにします。
ここでは delay
に1000ミリ秒 = 1秒を指定することで 2秒の時点で点く → 3秒の時点で消える → 4秒の時点で点く → 5秒の時点で消える という動作を実現しています。
終わりに
今回は定期的に実行するところを書きました。
次は割り込みでイベントを実行させるところを書きたいと思います。
明日は @toyowata さんです。よろしくお願いします。