STM32F7 Discoveryでmbed_blinkyとかが動いた
DISCO_F746NGはmbedプラットフォームに登録されていますが、Pre-order のリンクがあるだけで、オンラインコンパイラに追加できません。しかしmbed-srcにはすでにDISCO_F746NGのソースがありますので、GCCでビルドして使えるかためしてみようと思います。
GCCはLaunchpadがすでにcortex-m7対応してるようなのですが、STM32cubeF7のデモアプリもビルドしたくて、SW4STM32をインストールしたので、こっちのGCCを使うことにしました。
mbed-srcをチェックアウトして、ビルドスクリプトでビルドします。private_settings.pyは無くてもよいですが、warningがでるので作っておきます。
$ git clone https://github.com/mbedmicro/mbed.git $ cd mbed $ touch workspace_tools/private_settings.py $ python workspace_tools/build.py -mDISCO_F746NG -tGCC_ARM DISCO_F746NG target is not yet supported by toolchain GCC_ARM DISCO_F746NG target supports ARM, uARM, IAR toolchains Completed in: (0.00)s Build skipped: * GCC_ARM::DISCO_F746NG
むむ、GCC_ARMでコンパイルできなくされているのでDISCO_F746NGの設定にGCC_ARMを追加してみます。それから、GCCのmcutypeにcortex-m7fというのが無いみたいなので、cortex-m7に変更してビルドします。
$ diff -U3 workspace_tools/targets.py.orig workspace_tools/targets.py --- workspace_tools/targets.py.orig 2015-07-24 17:29:50.254856800 +0900 +++ workspace_tools/targets.py 2015-07-24 17:42:23.361956100 +0900 @@ -769,9 +769,9 @@ class DISCO_F746NG(Target): def __init__(self): Target.__init__(self) - self.core = "Cortex-M7F" + self.core = "Cortex-M7" self.extra_labels = ['STM', 'STM32F7', 'STM32F746', 'STM32F746NG'] - self.supported_toolchains = ["ARM", "uARM", "IAR"] + self.supported_toolchains = ["GCC_ARM", "ARM", "uARM", "IAR"] self.default_toolchain = "uARM" self.detect_code = ["0815"] $ python workspace_tools/build.py -mDISCO_F746NG -tGCC_ARM --silent Building library CMSIS (DISCO_F746NG, GCC_ARM) [Warning] stm32f7xx_hal_pcd.c@1152: In function 'PCD_WriteEmptyTxFifo': comparison between signed and unsigned integer expressions [-Wsign-compare] [Warning] stm32f7xx_hal_pcd.c@1167: In function 'PCD_WriteEmptyTxFifo': comparison between signed and unsigned integer expressions [-Wsign-compare] Building library MBED (DISCO_F746NG, GCC_ARM) [Warning] i2c_api.c@417: In function 'i2c_slave_read': array subscript has type 'char' [-Wchar-subscripts] Library: libmbed.a Completed in: (58.12)s Build successes: * GCC_ARM::DISCO_F746NG
とビルドはとおりました。次にこのmbedライブラリのテストプログラムをビルドしてみます。テストプログラムのビルドはmake.pyで行います。-p0 はテストプログラム番号です。-dオプションで直接書き込みます。
$ python workspace_tools/make.py -mDISCO_F746NG -tGCC_ARM -p0 -d /mnt/mbed Building project BASIC (DISCO_F746NG, GCC_ARM) Compile: main.cpp Compile: test_env.cpp Link: basic Elf2Bin: basic Image: ~/work/mbed/build/test/DISCO_F746NG/GCC_ARM/MBED_A1/basic.bin
動かないサンプルもありますが、ひどいことにはならなそうなので、次はmbed_blinkyをビルドしてみます。
$ mkdir mbed_blinky $ sh <<SOURCE #include "mbed.h" DigitalOut myled(LED1); int main() { while(1) { myled = 1; wait(0.2); myled = 0; wait(0.2); } } SOURCE $ python workspace_tools/make.py -m DISCO_F746NG -t GCC_ARM --source ~/work/mbed/mbed_blinky --buid ~/work/mbed/mbed_blinky -d /mnt/mbed Building project MBED_BLINKY (DISCO_F746NG, GCC_ARM) Compile: main.cpp Compile: test_env.cpp Link: mbed_blinky Elf2Bin: mbed_blinky Image: ~/work/mbed/mbed_blinky/mbed_blinky.bin
これでmbed_blinkyが動きました。次は他のI/Fも触っていきたいと思います。
STM32F746G-DISCO きたよー
STM32F7 の Discovery ボードが Digikey から届きましたよー。
mbed-mruby を載せて遊ぼうと思いましたが、mbed プラットフォーム登録されただけでまだオンラインコンパイラに登録できないようです。憤慨してる人たちのスレも立っているし。怒りの矛先は GR-PEACH にも。GR-PEACH はコンパイラ登録できるし、海外でも入手してる人いるようなのに、とばっちりですね。
USB mini-B つなぐだけで、書き込み済みのタッチパネルアプリが動きました。
mbed はいつ使えるかわからないので、まずは、STM32CubeMX の吐くコードと、STM32CubeF7 のサンプル眺めてみるところから始めてみます。
mbed-mruby GVL版マルチスレッドのヒープ使用量を調べた
しばらく間が空いたけれども、mbed-mrubyのスレッドの話の続きです。
下のmrubyのGVLでのマルチスレッド実装がされていますので、これをmbedにのせてみました。目的はmaster版とのスレッドが動いている時のヒープ使用量の比較です。
mirb - Embeddable Interactive Ruby Shell ================================================== size = 207299, acnt = 3860, fcnt = 562, max = 213044 ================================================== > a = DigitalOut.new LED1 => #<DigitalOut:0x200b34c8> ================================================== size = 214774, acnt = 3884, fcnt = 570, max = 263204 ================================================== > b = DigitalOut.new LED2 => #<DigitalOut:0x200b3360> ================================================== size = 222254, acnt = 3907, fcnt = 578, max = 270684 ================================================== > c = DigitalOut.new LED3 => #<DigitalOut:0x200b32d0> ================================================== size = 229748, acnt = 3931, fcnt = 586, max = 278178 ================================================== > t1 = Thread.new do * while 1 * a.toggle * wait 1 * end * end => #<Thread:0x200b3198> ================================================== size = 248814, acnt = 3992, fcnt = 607, max = 309523 ================================================== > t2 = Thread.new do * while 1 * b.toggle * wait 2 * end * end => #<Thread:0x200b2fe8> ================================================== size = 267892, acnt = 4053, fcnt = 628, max = 328601 ================================================== > t3 = Thread.new do * while 1 * c.toggle * wait 3 * end * end => #<Thread:0x200b2f28> ================================================== size = 286982, acnt = 4116, fcnt = 651, max = 347691 ================================================== > t1.join => nil ================================================== size = 290201, acnt = 4138, fcnt = 668, max = 347691 ================================================== > t2.join => nil ================================================== size = 293420, acnt = 4160, fcnt = 685, max = 347691 ================================================== > t3.join => nil ================================================== size = 296639, acnt = 4182, fcnt = 702, max = 349366 ================================================== >
size がヒープ使用量ですが、VMコピー版マルチスレッドは1スレッド起動毎にVM分の200Kくらいのヒープをとっていたけれど、こちらは全然増えません。
うーん、こっちのが良いですが、GVLってシングルコアの場合も何か不利益あるんだろうか。MPCoreのmbedでたら、もう一度も考えなおさないといけないけど、今後M7のmbedがでてきてもシングルコアだからなあ。
今回、理解せずひとまず載せてしまったので、ちゃんとmasterとどう違うか把握したいのと、次は速度面の比較もしたいです。
mbed で非同期メッセージドリブンなスレッドクラスを作った(2)
sabme.hatenablog.com
前の記事で書いた通り、subscribeでメンバ関数以外も登録できるようにしてみました。
実行結果は前回と同じ。自由度は高いけど、これはこれでどうかと思う。
#include <stdlib.h> #include <string> #include "mbed.h" #include "rtos.h" #include "Actor.h" Serial pc(USBTX, USBRX); Timer tim; class Task : public Actor { public: static const MessageID MSG_JOB_START = 0x01; void setPriority(osPriority lv) { thread.set_priority(lv); } }; Task *taskA; Task *taskB; Task *taskC; void jobA(void *p) { int cnt = rand() % 100000000; printf("%6d [TaskA] start\n", tim.read_ms()); for (int i = 0; i < cnt; i++) { /* nop */ } printf("%6d [TaskA] end\n", tim.read_ms()); sendMail(taskB, Task::MSG_JOB_START, NULL); } void jobB(void *p) { int cnt = rand() % 100000000; printf("%6d [TaskB] start\n", tim.read_ms()); for (int i = 0; i < cnt; i++) { /* nop */ } printf("%6d [TaskB] end\n", tim.read_ms()); sendMail(taskC, Task::MSG_JOB_START, NULL); } void jobC(void *p) { int cnt = rand() % 100000000; printf("%6d [TaskC] start\n", tim.read_ms()); for (int i = 0; i < cnt; i++) { /* nop */ } printf("%6d [TaskC] end\n", tim.read_ms()); } int main() { tim.start(); taskA = new Task(); taskA->subscribe(Task::MSG_JOB_START, jobA); taskB = new Task(); taskB->subscribe(Task::MSG_JOB_START, jobB); taskC = new Task(); taskC->subscribe(Task::MSG_JOB_START, jobC); taskC->setPriority(osPriorityHigh); for (int i=0; i<5; i++) { sendMail(taskA, Task::MSG_JOB_START, NULL); // input job } while (true) {} }
mbed で非同期メッセージドリブンなスレッドクラスを作った
mbed rtosのThreadクラスはそのままでも簡単ではあるのですが、Thread と Mail をより簡単に使うための非同期処理用スレッドクラス Actor を作りました。
http://developer.mbed.org/users/mzta/code/actor/
サンプル1
まず、イベントドリブンにするためのイベント登録処理のサンプルです。
#include "mbed.h" #include "Actor.h" class Person : public Actor<Person> { public: Person() { subscribe(MSG_GREET, &Person::greet); } void greet(void *p) { printf("Hi! %s\n", (const char *)p); } enum Messages { MSG_GREET = 0x01 }; }; int main() { Person *taro = new Person(); sendMail(taro, Person::MSG_GREET, (void *)"Jiro"); while (true) {} } /** * execution result : * * > Hi! Jiro * > */
Actor テンプレートクラスから、Person クラスを作成して、そのコンストラクタでメンバ関数 Person::greet() を Person::MSG_GREET というメッセージIDに登録しています。
main 関数では、Person インスタンス taro を作成して、taro に Person::MSG_GREET のメールを送ります。メールでは任意のデータポインタを渡せるので、ここではconst char * "Jiro"を渡します。メールを受信した taro スレッドは、greet() をコールして "Hi! Jiro" を出力しますが、このサンプルでは別スレッドで動いているのがわかりません。次の非同期処理のテストで確認します。
サンプル2
RTOS で非同期処理をやりたいのってどんなシーンでしょう。ストリーミングデータをパケット解析して、逐次上位に渡すようなバケツリレーの処理を思いつくのですが、今回は時間がかかる処理をパイプラインのように処理するようなサンプルを作ってみました。
Taskクラスは、MSG_JOB_START を受けて job() を実行し、完了したらpipelineの次のTaskインスタンスに MSG_JOB_START メッセージを送るクラスです。job() はループで時間稼ぎするだけの無意味な関数で、開始時刻、終了時刻を出力します。TaskA, B, C の3つのタスクを用意し、3段パイプラインのような動きをするテストです。TaskCだけプライオリティを高くしています。
#include <stdlib.h> #include <string> #include <list> #include "mbed.h" #include "Actor.h" #include "rtos.h" Serial pc(USBTX, USBRX); Timer tim; class Task : public Actor<Task> { public: typedef std::list<Task *> Pipeline; Task(const char *name_str) : name(name_str) { subscribe(MSG_JOB_START, &Task::job); } void job(void *p) { int cnt = rand() % 100000000; printf("%6d [%s] start\n", tim.read_ms(), name.c_str()); for (int i = 0; i < cnt; i++) { /* nop */ } printf("%6d [%s] end\n", tim.read_ms(), name.c_str()); Pipeline *pipeline = (Pipeline *)p; if (!pipeline->empty()) { Task *next = pipeline->front(); pipeline->pop_front(); sendMail(next, MSG_JOB_START, pipeline); } else { delete pipeline; } } static const MessageID MSG_JOB_START = 0x01; private: string name; public: void setPriority(osPriority lv) { thread.set_priority(lv); } }; int main() { tim.start(); Task *taskA = new Task("TaskA"); Task *taskB = new Task("TaskB"); Task *taskC = new Task("TaskC"); taskC->setPriority(osPriorityHigh); for (int i=0; i<5; i++) { Task::Pipeline *pipeline = new Task::Pipeline(); pipeline->push_back(taskB); pipeline->push_back(taskC); sendMail(taskA, Task::MSG_JOB_START, pipeline); // input job } while (true) {} } /** * execution result : * * > 4 [TaskA] start * > 509 [TaskA] end * > 538 [TaskA] start * > 544 [TaskB] start * > 636 [TaskA] end * > 678 [TaskB] end * > 679 [TaskA] start * > 713 [TaskC] start * > 1215 [TaskC] end * > 1237 [TaskB] start * > 1341 [TaskA] end * > 1385 [TaskA] start * > 1819 [TaskA] end * > 1863 [TaskA] start * > 2559 [TaskB] end * > 2603 [TaskC] start * > 2890 [TaskC] end * > 2908 [TaskB] start * > 3341 [TaskA] end * > 3867 [TaskB] end * > 3897 [TaskC] start * > 3952 [TaskC] end * > 3970 [TaskB] start * > 4831 [TaskB] end * > 4860 [TaskC] start * > 5276 [TaskC] end * > 5294 [TaskB] start * > 5818 [TaskB] end * > 5847 [TaskC] start * > 6033 [TaskC] end */
図にすると、投入された5つのジョブがパイプラインのように実行できました。
まとめ
スレッド毎にクラスを定義してやる必要がありますが、簡単にメッセージドリブンのスレッドを作ることができました。
なぜこれを作ったかというと、mruby-mbed でこんな感じのスレッドを試したかったからなのですが、subscribe でメンバ関数しか登録できないのは不便なので、テンプレートクラスをやめて、外の関数ポインタが登録できるようにした方が良い気がしてきました。
mrbgemsをmruby-mbedだけにしたらVM1インスタンスで100Kbyteくらいになった
タイトルは本文。
mbed-mruby で mruby がとってるヒープサイズを調べた - くくしたいブログ
の続きで、mrbgems削ったら、ヒープ使用量が半分くらいになりました。
どんな機能が無くなったのかは把握できていません。
mirb - Embeddable Interactive Ruby Shell > ================================================== size = 109640, acnt = 1153, fcnt = 18, max = 110356 ================================================== > a = DigitalOut.new LED1 => #<DigitalOut:0x200b21a8> > b = Ticker.new => #<Ticker:0x200b2130> > b.attach 1, a do |c| * c.toggle * end => #<Ticker:0x200b2130> > ================================================== size = 249843, acnt = 2411, fcnt = 68, max = 265900 ================================================== > b.detach => #<Ticker:0x200b2130> > ================================================== size = 160543, acnt = 2434, fcnt = 1202, max = 305741 ==================================================