読者です 読者をやめる 読者になる 読者になる

mbed OSのスケジューラー MINAR を使ってみる

mbed Advent Calendar 2015 の1日目の記事です。

注: この記事は technology preview 版のソフトウェアを利用したものになるので、今後変わる可能性があります。

f:id:mia_0032:20151026214417p:plain

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 さんです。よろしくお願いします。