初めに
Spresenseの強みは、マルチコアなんじゃないかと常々思っていたところで、「それぞれのコアからI2Cの異なるセンサーをアクセスできるの?」という疑問がわいてきたので、できるのかな~と思ってやってみました。
使用したもの
・SpresenseのMainボード
・SPRESENSE用3軸加速度・3軸ジャイロ・気圧・温度センサ アドオンボード
※販売終了品です。
ブログ(元ネタ)
この記事は、以下のブログへ過去に投稿したもののまとめです。
マルチコアから呼び出す課題
I2Cデバイスは、ご存じの通り複数のslaveデバイスが接続でき、アドレスで振り分けるので、ライブラリも複数呼び出しアドレスでフィルタ出来るから、うまく制御できれば動かせるかも?と思っていたんだけど、実はそう簡単にはいかない問題がありました。
何かというと、SpresenseのHW I2Cを制御するドライバは、I2Cからの割り込みを利用しているため、複数のコアで動かそうとすると、予期せぬ割り込みが入ってしまうため、システム的にはエラーが発生してしまいます。
すなわち、双方のコアが自分以外の処理を行っている際に、この割り込みを禁止しなければなりません。となると、Spresense SDKの中で割り込みの許可・禁止を行っているAPIを自分の通信を行っている以外は呼び出す必要があります。
この割り込みの許可・禁止は、この関数内で行っています。
となると、この関数(cxd56_i2cbus_initialize / cxd56_i2cbus_uninitialize)をアクセスのたびに呼び出す必要があります。
で、Wireライブラリを見てみると、
なんと、(begin / end)で、呼んでいます!
となれば、以下を守ることで、マルチコアでそれぞれの別なセンサーにアクセスできることがわかりました。
Arduinoライブラリを使いたい! (12/07 更新!)
何とか、WireというArduinoの標準IFライブラリで呼べるようになったけど、実際には、各ボードベンダーが出しているWireライブラリの上に載っているデバイスライブラリを呼べるようにならないと、全部自分でライブラリを書き直すことになってしまい、非常にモチベーションが下がる状況ですよね。
ということで、今一度、課題を整理した上で、問題となっているのが、自分でないコアがI2Cアクセスを行った際に、予期せぬ割り込みが入ってしまうことならば、Wire全体を無効にするのではなく、割り込みだけをマスクすればいいんじゃないか?ということで、begin/endを呼んでいる部分を割り込みを禁止する
up_enable_irq(CXD56_IRQ_SCU_I2C0);
up_disable_irq(CXD56_IRQ_SCU_I2C0);
にしてしまえば、すなわち、自分が読み出す際にのみ、割り込みを許可すればよいのでは?ということで、そのように変更することで、BMI160もBMP280も、Arduinoライブラリからの読み出しで読めるようになりました!
これで、Arduinoライブラリがあれば、どのデバイスでもマルチコアで呼べるし、3コア、4コアでも呼べるようになりました!
サンプルコード
上記の課題がわかったので、MainCoreで、BMI160のアクセスを、
SubCoreで、BMP280のアクセスをするサンプルコードを書いてみました。
それぞれのセンサーへのアクセスは、メッセージ通信を利用して、交互に行うこととして、同時アクセスをしないようにすることで、実際にそれぞれのコアでデータを読むことができるサンプルが作れました!
所感
ただし、実際のArduinoで使われているBMI160/BMP280のライブラリは、wire.end()を呼び出すend処理がないため、現時点ではそのまま使えませんでした。
何とかライブラリを呼び出して使えるようにしたいな~とおもいつつ、
ここまで苦労してマルチコアで呼び出すくらいなら、センサーデータを読むだけのコアを1つ作って、そのコアでは読み出しだけを行い、信号処理をサブコアに振り分けた方がよいのでは~、と思っていたりもします…。
これで、Arduinoライブラリがあれば、どのデバイスでもマルチコアで呼べるし、3コア、4コアでも呼べるようになりました!
いよいよ、Spresenseのマルチコアが活きてくるようになるかなあ~。