SONYのマイコンであるSpresenseではMultiCoreライブラリ(MPライブラリ)を使うことでマルチコアプログラミングができます.
ただし,直接データの値をやりとりできるのはuint_32t
のスカラー値のみなので,例えば配列変数を送りたいケースではアドレスを送受信する必要があります.
このアドレス送受信を私が実装する上で,ポインタの記述で悩んだ点があったので記事にしておきます.
間違いや不適切な点があればお知らせください.
ドキュメントの内容
Spresense Arduino IDEのマニュアルを読むと,アドレス送受信の項を読むと関数の定義は以下のようになっています.
- 送信関数:
int Send(int8_t msgid, void *msgaddr, int subid)
- 受信関数:
int Recv(int8_t *msgid, void *msgaddr, int subid)
Send
関数の第二引数とRecv
関数の第二引数がそのまま対応しているようにも見えますが,ここが以下のように書き間違えたポイントでした.
間違った書き方
#include <MP.h>
#ifndef SUBCORE // maincore
double a[2] = {1.5, 2.0};
void setup() {
MP.begin(1);
MP.RecvTimeout(MP_RECV_BLOCKING);
delay(300);
MP.Send(0, &a, 1);
}
void loop() { }
#endif
#if (SUBCORE == 1)
double* b;
void setup() {
MP.begin();
MP.RecvTimeout(MP_RECV_BLOCKING);
uint8_t msgid_ = 255;
MP.Recv(&msgid_, b);
}
void loop() {
MPLog("%lf, %lf\n", b[0], b[1]);
delay(1000);
}
#endif
メインコアで宣言したdouble a[2]
のアドレスをサブコア1に送り,double* b
に格納して表示したいプログラムです.
サブコア1,メインコアを選択してそれぞれ書き込みます
コンパイルは問題なく通りますが,シリアルモニタの出力を見ると正しく値を受け取れていないことがわかります.
[Sub1] 0.000000, 0.000000
正しい書き方
#include <MP.h>
#ifndef SUBCORE // maincore
double a[2] = {1.5, 2.0};
void setup() {
MP.begin(1);
MP.RecvTimeout(MP_RECV_BLOCKING);
delay(300);
MP.Send(0, &a, 1);
}
void loop() { }
#endif
#if (SUBCORE == 1)
double* b;
void setup() {
MP.begin();
MP.RecvTimeout(MP_RECV_BLOCKING);
uint8_t msgid_ = 255;
//MP.Recv(&msgid_, b);
MP.Recv(&msgid_, &b); // bそのものではなくポインタを渡すように修正
}
void loop() {
MPLog("%lf, %lf\n", b[0], b[1]);
delay(1000);
}
#endif
シリアルモニタの出力結果は以下の通りです.
[Sub1] 1.500000, 2.000000
正しく値を受け取れていることが分かります.
何がおかしいのか
最初間違えたコードでは,Recv
関数の第二引数としてdouble *
型を与えているので,ポインタではあるためコンパイルは問題なく通ります.
しかし,通信がアドレスを送受信していることを考えると,Recv
関数の引数にはアドレスを格納する変数のポインタを与える必要があります.
Recv
関数の第二引数は,アドレスをどこに格納すればいいかを聞いているのです.
したがって先のプログラムでは,アドレスを格納する変数であるdouble* b
の場所を&b
として教えてやる必要があります.
Send
では変数のアドレス(ポインタ)を渡せばいいのに対して,Recv
ではポインタのアドレス(ポインタのポインタ)を渡すという点で,注意が必要です.
応用
特定のコアの特定のメッセージIDからのみアドレスを受け取る関数を作りたいときは以下のように書くことができます.
メインコア側の送信プログラムは同じなので省略しています.
#if (SUBCORE == 1)
double* b;
void receiveWithID(uint8_t msgid_, double** pointer_){
uint8_t msgid_receive_;
double* addr_;
while(1){
MP.Recv(&msgid_receive_, &addr_);
if (msgid_receive_==msgid_){
*pointer_ = addr_;
break;
}
}
}
void setup() {
MP.begin();
MP.RecvTimeout(MP_RECV_BLOCKING);
const uint8_t REQUIRED_MSG_ID = 0;
receiveWithID(REQUIRED_MSG_ID, &b);
}
void loop() {
MPLog("%lf, %lf\n", b[0], b[1]);
delay(1000);
}
#endif
終わりに
最初から公式のサンプルコードであるMessegeHelloを参考にすべきでした…
ここから発展してstd::vector
をコア間でやりとりしようとして失敗したときの話はこちら