概要
SPRESENSEでマルチコアを使用したプログラムの実装を試し、動作を確認した。
背景と目的
ある目的で、SPRESENSEでマルチコアを使用したプログラムの実装をする必要が出た。そこで、まずは簡単なサンプルをもとに自分なりに理解しながら、実装し動作を確認してみる。
詳細
0. 参考資料
1. 作成するプログラムの概要
- Arduino IDE
- SPRESENSE Arduino SDK
- SPRESENSE Arduino SDKのサンプルにある Examples > MultiCore MP > Message > MessageHelloスケッチを参考
- サブコア1からメッセージとして以下の構造体データを引き渡す
実践的なデータとして、構造体をやり取りできたほうが良いので、MessageHelloを基にした。
struct MyPacket { volatile int status; /* 0:ready, 1:busy, 互いに書き込みをするためvolatile */ char message[MSGLEN]; };
2. メインコアのプログラム
ポイントをまとめると、
- setupにて、MP.begin(N)としてSubCoreNの起動を行う
- MP.RecvTimeoutで受信モード設定
- loopにて、MyPacket型構造体のポインタを定義し、MP.Recvで受けとる
- 受け取ったら、statusを0にし、受け取り完了=> ready状態をSubCore1に伝える
#include <MP.h> #define MSGLEN 64 #define MY_MSGID 10 struct MyPacket { volatile int status; /* 0:ready, 1:busy, 互いに書き込みをするためvolatile */ char message[MSGLEN]; }; void setup() { Serial.begin(115200); while (!Serial); // サブコアの起動 int ret = MP.begin(1); if (ret < 0) { printf("MP.begin(%d) error = %d\n", 1, ret); } // 受信モードの設定: MP_RECV_POLLING=データの受信をポーリングするモード // Recv() を呼び出したときに受信データが無かった場合はすぐに抜けます。このモードでは受信待ちに入ることはありません。 MP.RecvTimeout(MP_RECV_POLLING); int usedMem, freeMem, largestFreeMem; MP.GetMemoryInfo(usedMem, freeMem, largestFreeMem); MPLog("Used:%4d [KB] / Free:%4d [KB] (Largest:%4d [KB])\n", usedMem / 1024, freeMem / 1024, largestFreeMem / 1024); } void loop() { int8_t msgid; MyPacket *packet; // サブコア側のMyPacketのポインタを受け取る // サブコアからメッセージを受け取る if (MP.Recv(&msgid, &packet, 1) > 0) { printf("id=%d, message=%s\n", msgid, packet->message); packet->status = 0; // status -> ready } }
3. サブコア1のプログラム
- 各種マクロ定義とMyPacket構造体は同様に参照する必要があるのでメインコアと同様に定義
- setupで、MP.begin()でメインコアに起動完了を伝える。なお、後述の書き込み対象Coreを設定しないと、MP.begin()はコンパイルエラーになる。※beginの引数が必要と怒られてしまう。
- loopで、char配列messageに文字列データを格納し、MP.Sendにてメインコアに送信
#include <MP.h> #define MSGLEN 64 #define MY_MSGID 10 struct MyPacket { volatile int status; /* 0:ready, 1:busy, 互いに書き込みをするためvolatile */ char message[MSGLEN]; }; MyPacket packet; // やり取りしたい対象のデータ, グローバルで宣言 void setup() { memset(&packet, 0, sizeof(packet)); MP.begin(); // Tools > CoreでSubCoreを選ぶと、引数なしのbeginがコンパイルエラーにならなくなる } void loop() { static int count = 0; // メインコアでpacket.status -> readyに書き換えられたら実行する if (packet.status == 0) { // メッセージ作成 packet.status = 1; // status -> busy snprintf(packet.message, MSGLEN, "[%s] Hello %d", "Sub1", count++); // メインコアにメッセージ送信 int ret = MP.Send(MY_MSGID, &packet); if (ret < 0) { printf("MP.Send error = %d\n", ret); } } delay(500); }
4. 書き込み
メインコアは、通常通り書き込みすればよい。 サブコアは、IDEのツールバーからTools > Coreで、SubCore1を選択。

5. 動作確認
以下のように、シリアルターミナルにメインコア側で受け取ったメッセージが出力された。
というわけで、無事動作確認OK。
[Main] Used: 896 [KB] / Free: 640 [KB] (Largest: 640 [KB]) id=10, message=[Sub1] Hello 0 id=10, message=[Sub1] Hello 1 id=10, message=[Sub1] Hello 2 id=10, message=[Sub1] Hello 3 id=10, message=[Sub1] Hello 4 id=10, message=[Sub1] Hello 5 id=10, message=[Sub1] Hello 6 id=10, message=[Sub1] Hello 7 id=10, message=[Sub1] Hello 8 id=10, message=[Sub1] Hello 9
まとめと今後の課題
SPRESENSEで、マルチコアのプログラムの実装方法の基本が整理できた。実装予定のプログラムでは、より複雑なデータをやり取りする必要があるが、今回の経験を役立てて完成させたい。















