TOPPERSプロジェクトは LEGO MINDSTORMS EV3 向けにリアルタイムOS環境 EV3RT を提供しています。EV3RT は TOPPERS/HRP2 カーネル をベースとしていますが、TOPPERS/HRP3 カーネル をベースにしたソースが既に GitHub に公開されています。
本日のネタは、この HRP3 版をインストールして使ってみることです。現行の(HRP2ベースの) EV3RT 開発環境は構築済みで、git コマンドも使えるようになっていることを前提に話を進めます。
Windows を使っている場合は Cygwin 環境に ruby もインストールしておいてください。configure や cfg が HRP3 では ruby スクリプトになったせいで、ruby コマンドが必須になっています。
ソースの取得とダイナミックローダのビルド
まずは、ソース取得のために git clone します。
$ git clone https://github.com/ev3rt-git/ev3rt-hrp3
ソースを取得できたら、以下の手順で HRP3 のダイナミックローダをビルドします。
$ cd ev3rt-hrp3
$ git submodule init
$ git submodule update
$ cd sdk/firmware
$ make img=loader
ビルドが完了すると uImage という名前でダイナミックローダができています。これを HRP2 版 EV3RT で作成した SD カード内にある uImage ファイルと入れ替えます。
できた SD カードを EV3 にセットして起動して、以下の画面が現れたら EV3 側のセットアップは完了です。
helloev3 アプリのビルド
取得したソースには SDK も含まれているので、HRP2 の場合と同様にアプリをビルドできます。
$ cd ev3rt-hrp3/sdk/workspace
$ make app=helloev3
macOS の場合、実害は無いようですが、ビルド中に以下のメッセージが何度も出てきます。
find: illegal option -- t
usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]
find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]
これは、HRP3 のビルド環境が GNU の find コマンドを前提としているため、macOS の find コマンドに無いオプション(ファイル/ディレクトリの種別を指定するオプション)でエラーになっています。ちょっとメッセージがうるさいので、私は Homebrew で findutils を入れて GNU の find を使うようにしました。
ビルドしてできた app ファイルを HRP2 と同様に EV3 に転送して起動すると以下の画面になります。このアプリでセンサーやモータの動作を確認できます。
C++ で書かれたアプリのビルド
GitHub から取得したソースには C++ API が含まれていないので、HRP2版からコピーします。
$ cp -pr hrp2/sdk/common/library/libcpp-ev3 ev3rt-hrp3/sdk/common/library/
これだけで C++ API が使えるようになります。HackEV を使って5秒おきに前進/更新を繰り返す簡単なアプリケーションを書いてみます。
$ cd ev3rt-hrp3/sdk/workspace
$ mkdir walker
以下4つのファイルを作成します。
- walker/app.h:
#ifdef __cplusplus
extern "C" {
#endif
#include "ev3api.h"
#define MAIN_PRIORITY TMIN_APP_TPRI + 1
#define WALKER_PRIORITY TMIN_APP_TPRI + 2
#ifndef STACK_SIZE
#define STACK_SIZE 4096
#endif /* STACK_SIZE */
#ifndef TOPPERS_MACRO_ONLY
extern void main_task(intptr_t exinf);
extern void walker_task(intptr_t exinf);
#endif /* TOPPERS_MACRO_ONLY */
#ifdef __cplusplus
}
#endif
- walker/app.cpp:
#include "app.h"
#include "Motor.h"
#include "TouchSensor.h"
using namespace ev3api;
bool isForwarding = true;
Motor* leftWheel;
Motor* rightWheel;
TouchSensor* touchSensor;
void walker_task(intptr_t exinf) {
if (touchSensor->isPressed()) { // 左ボタン押下でメインを起こす
wup_tsk(MAIN_TASK);
} else { // 走行
if(isForwarding) {
leftWheel->setPWM(25);
rightWheel->setPWM(25);
isForwarding = false;
} else {
leftWheel->setPWM(-25);
rightWheel->setPWM(-25);
isForwarding = true;
}
}
ext_tsk();
}
void main_task(intptr_t unused)
{
leftWheel = new Motor(PORT_C);
rightWheel = new Motor(PORT_B);
touchSensor = new TouchSensor(PORT_1);
sta_cyc(WALKER_CYC);
slp_tsk(); // 起きたら、走行をやめる
stp_cyc(WALKER_CYC);
leftWheel->stop();
rightWheel->stop();
ext_tsk();
}
- walker/app.cfg:
INCLUDE("app_common.cfg");
#include "app.h"
DOMAIN(TDOM_APP) {
CRE_TSK( MAIN_TASK, { TA_ACT, 0, main_task, MAIN_PRIORITY, STACK_SIZE, NULL } );
CRE_CYC( WALKER_CYC, { TA_NULL, { TNFY_ACTTSK, WALKER_TASK }, 2000U * 1000U, 1U * 1000U });
CRE_TSK( WALKER_TASK, { TA_NULL, 0, walker_task, WALKER_PRIORITY, STACK_SIZE, NULL });
}
ATT_MOD("app.o");
- walker/Makefile.inc:
mkfile_path := $(dir $(lastword $(MAKEFILE_LIST)))
APPL_COBJS +=
SRCLANG := c++
ifdef CONFIG_EV3RT_APPLICATION
# Include libraries
include $(EV3RT_SDK_LIB_DIR)/libcpp-ev3/Makefile
endif
以下でビルドします。
$ make app=walker
ビルドしてできた app ファイルを EV3 に転送して起動すると、以下のように前進と更新を繰り返します。止めるにはタッチセンサを押下します。
おわりに
今のところ、HRP2 ベースの EV3 アプリを HRP3 ベースに移植する上で修正が必要だったのは以下2点です。
- タイマーの時間指定が msec から usec になったので、従来の時間指定を 1000 倍する必要あり
- EV3_CRE_CYC は無くなった。ユーザドメインの周期処理がサポートされたので、代わりにこれを使う
- Makefile.inc で APPL_DIR へのフォルダ追加は、HRP3 ではエラーになるので、代わりに APPL_DIRS を使う