SPRESENSEのメインボードと拡張ボードのI2Cは共有バスです。一方、メインボードのIO電圧は1.8Vで拡張ボードのIO電圧は3.3Vです。原理的には、メインボードにつながれたI2Cデバイスと拡張ボードにつながれたI2Cデバイスは同時に使えるはずです。
でも、考えてみれば、今までやったことがないなということに気づきました。ということで、今回試してみたいと思います。
SPRESENSEとI2Cデバイスの接続
メインボードに接続するI2Cデバイスには、BMI160アドオンボード(もう手に入らない貴重品)。拡張ボードに接続するI2Cデバイスには、128x32のOLEDディスプレイデバイスを接続してみました。
まずはデバイスが認識するかどうか I2CScanner で確認してみました。Arduino IDE の"スケッチ例"からSPRESENSE用の「I2CScanner」があるので、それを開いて接続を確認します。念のためコードを示しておきます。
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(115200);
}
void loop() {
uint8_t error;
int nDevices;
int addr;
int upper, lower;
printf("I2C Scanning...\n\n");
nDevices = 0;
printf(" -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F\n");
for (upper = 0; upper < 8; upper++) {
printf("%d- :", upper);
for (lower = 0; lower < 16; lower++) {
addr = upper * 16 + lower;
Wire.beginTransmission(addr);
error = Wire.endTransmission();
if (error) {
printf(" --");
} else {
printf(" %02x", addr);
nDevices++;
}
}
printf("\n");
}
printf("\nthe number of found I2C devices is %d.\n\n", nDevices);
delay(5000);
}
スキャンした結果は以下の通りです。予想したとおりメインボードと拡張ボードそれぞれに接続されたI2Cデバイスを認識することができました。
I2C Scanning...
-0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F
0- : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
1- : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
2- : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
3- : -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
4- : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
5- : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
6- : -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
7- : -- -- -- -- -- -- 76 -- -- -- -- -- -- -- -- --
the number of found I2C devices is 3.
実際に両方のI2Cデバイスを使ってみた
SPRESENSEにつながれているI2Cデバイスを実際に動作させてみました。アプリケーションは、ジャイロセンサーの値をOLEDディスプレイに表示する単純なものです。
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <BMI160Gen.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
float convertRawGyro(int gRaw) {
// since we are using 250 degrees/seconds range
// -250 maps to a raw value of -32768
// +250 maps to a raw value of 32767
float g = (gRaw * 250.0) / 32768.0;
return g;
}
void setup() {
Serial.begin(115200);
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
display.clearDisplay();
BMI160.begin(BMI160GenClass::I2C_MODE);
uint8_t dev_id = BMI160.getDeviceID();
Serial.print("IMU DEVICE ID: ");
Serial.println(dev_id, HEX);
BMI160.setGyroRange(250);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0,0);
display.print("IMU DEVICE ID: 0x");
display.println(dev_id, HEX);
display.display();
delay(5000); // wait 5sec
}
void loop() {
int gxRaw, gyRaw, gzRaw; // raw gyro values
float gx, gy, gz;
char buf[16];
BMI160.readGyro(gxRaw, gyRaw, gzRaw);
gx = convertRawGyro(gxRaw);
gy = convertRawGyro(gyRaw);
gz = convertRawGyro(gzRaw);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0,0);
sprintf(buf, "%+02.2f", gx);
display.println("roll: " + String(buf));
sprintf(buf, "%+02.2f", gy);
display.println("pitch: " + String(buf));
sprintf(buf, "%+02.2f", gz);
display.println("yaw: " + String(buf));
display.display();
delay(500);
}
実際に動かしている様子です。なんの問題もなく使えてますね。
使ってみた感想
頭ではわかっていたものの、本当に使えるのかいな?と一抹の疑問がありました。しかし、何事も実際にやってみるものですね。その疑問は払拭されました。最近は1.8Vのセンサーも増えてきたのでこの技は結構使えるかも知れません。試していませんが、LTE拡張ボードでも同様の回路になっているので問題なく使えると思います。LTE拡張ボードならボードサイズも小型ですのでもっと見栄えも応用範囲も広がりそうです。