ESP32 の SPI Read
目的
ESP32 の SPI 通信でセンサの値を取得してみた
何処かで誰かやってるだろうと思ってググったが誰もやっていない
みんな$ \rm I^2C好きね
SPIはLCDのためのインターフェースだけじゃないんだぞ
環境等
Arduino IDE
Visual Studio Code 拡張機能
接続したセンサ
LSM9DS1
STMicro 製 9軸 IMUセンサ
Sparkfun 製breakout ボードを使用
4線式SPI(SS,MOSI,MISO,CLK)
加速度/ジャイロセンサと地磁気センサで2つのスレーブセレクト(SS)端子がある
データシート等
コード
とりあえずコードを載せる
基本的なコード上の手続きはArduinoとさほど変わらない
code:spi_test.ino
#include <SPI.h>
static const int spiClk = 1000000; // クロック 1 MHz
SPIClass * vspi = NULL; // VSPIを使用する
void setup() {
vspi = new SPIClass(VSPI); // VSPI にSPIクラスをインスタンス
vspi->begin(21,18,19); // SCK,MISO,MOSI,(SS=-1)
pinMode( 5, OUTPUT); // VSPI SS AG
pinMode(17, OUTPUT); // VSPI SS M
Serial.begin(115200);
}
void loop() {
vspiCommand();
delay(100);
}
void vspiCommand() {
// 加速度センサとジャイロセンサ側(AG)のWHO_AM_Iレジスタの読み取り
byte adr 2= {0x8F,0}; // アドレス =(0x0F:WHO_AM_I | 0x10:read accsess)
vspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE3)); // SPI通信設定
digitalWrite(5, LOW); // AG SS を LOW
vspi->transfer(adr,2); // アドレス送信 & 読み取り値格納
digitalWrite(5, HIGH); // AG SS を HIGH
vspi->endTransaction(); // SPI 通信終了
Serial.print("accgyr adr0=0x");
Serial.print(adr0,HEX); // FF となっている
Serial.print("\n");
Serial.print("accgyr adr1=0x");
Serial.print(adr1,HEX); // 読み取った値 0x68 が入っている
Serial.print("\n");
// 地磁気センサ側(M)のWHO_AM_Iレジスタの読み取り
adr 0= 0x8F; // アドレス(0x0F:WHO_AM_I | 0x10:read accsess)
adr 1= 0x00; // 一応0に戻しておく
vspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE3)); // SPI 通信設定
digitalWrite(17, LOW); // M SS を LOW
vspi->transfer(adr,2); // アドレス送信 & 読み取り値格納
digitalWrite(17, HIGH); // M SS を HIGH
vspi->endTransaction(); // SPI 通信終了
Serial.print("mag adr0=0x");
Serial.print(adr0,HEX); // 上と同じ。FF
Serial.print("\n");
Serial.print("mag adr1=0x");
Serial.print(adr1,HEX); // 読み取った値 0x3D が入っている
Serial.print("\n");
}
説明
SPI 通信について
SPI 通信と一言に言っても4種の方式があり、デバイスのよって様々なのでよく確認すること
公式に案内がなかったが、Arduinoの方ではこうなっている
table:SPI_MODE
Mode CLK初期値 出力CLKエッジ 読み出しCLKエッジ
SPI_MODE0 0 立ち下がり 立ち上がり
SPI_MODE1 1 立ち上がり 立ち下がり
SPI_MODE2 0 立ち上がり 立ち下がり
SPI_MODE3 1 立ち下がり 立ち上がり
今回はSPI_MODE3であった
このMODE0~3の番号は特に何か規格で決まっているものでもないため、別環境では全く意味を為さない
書き込み側が立ち下がりエッジで出力し、読み出し側は次の立ち上がりエッジで読み取る
こうすることで、ビット遷移時の不定状態を読み出さないようにできる
次に波形を示す
信号と波形(詳しくはデータシートを参照)
code:wave.txt
ライト(write;書き込み)時
SS_AG """""""""|____________________________________________________________________|"""
CLK """"""""""""|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"""""""
MOSI ============Xr/wXa6 Xa5 Xa4 Xa3 Xa2 Xa1 Xa0 Xd7 Xd6 Xd5 Xd4 Xd3 Xd2 Xd1 Xd0 X=====
MISO ==================================================================================
リード(read;読み出し)時
SS_AG """""""""|____________________________________________________________________|"""
CLK """"""""""""|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"|_|"""""""
MOSI ============Xr/wXa6 Xa5 Xa4 Xa3 Xa2 Xa1 Xa0 X=====================================
MISO ============================================Xd7 Xd6 Xd5 Xd4 Xd3 Xd2 Xd1 Xd0 X=====
SS
スレーブセレクト
LOW で通信相手を選択
CLK
クロック
デフォルトHIGH
立ち上がりエッジでのデータが有効
MOSI
Master Out Slave In
(センサ側から見た)データ入力線
加速度/ジャイロセンサ
1バイト目
table:1byte
ビット 7 6 : 0
名前 R/W A6~A0
説明 リード/ライトモード選択 アドレス
リード/ライトモード選択ビット
リード(読み出し)時に1にする
ライト(書き込み時)は0
アドレス
データシートにあるレジスタマップを参照する
今回は動作実験用に必ず固定値が読み出せるWHO_AM_Iレジスタ0x0Fを使用した
2バイト目以降は書き込み時にデータを挿入する
地磁気センサ
1バイト目
table:1byte
ビット 7 6 5 : 0
名前 R/W MS A5~A0
説明 リード時1 アドレスインクリメント アドレスビット
リード/ライトモード選択ビット
リード(読み出し)時に1にする
ライト(書き込み時)は0
アドレスインクリメントビット
連続バイトを読み書きするとき、1にしておくと自動でアドレスをインクリメントしてくれる
0の時はおそらく同じレジスタに繰り返し書かれる(または読まれる)事になる(未確認)
アドレス
データシートにあるレジスタマップを参照する
今回は動作実験用に必ず固定値が読み出せるWHO_AM_Iレジスタ0x0Fを使用した
2バイト目以降は書き込み時にデータを挿入する
MISO
Master In Slave Out
(センサ側から見た)データ出力線
本センサでは読み出しモード時に2byte目以降のクロックでデータが出力される
上記コードでadr[1]に読み出しデータが返ってきていた理由はここにある
実際の波形
Analog Dicovery 2 にてSPI 波形をキャプチャした
3線式SPIにしか対応していないため、MOSI(上の段)とMISO(下の段)をそれぞれ別のSPIとして見せている
上二つが地磁気センサ
下二つが加速度/ジャイロセンサである
https://gyazo.com/fbacd08671486fc9cffefd4582268df6
そのほか
上記コードでは1MHzとしたが、SPIクロック周波数は10MHzまで動作確認した
$ \rm I^2Cと比較して、規格が十分ではないなどの多少の繁雑性はあれども、この高速通信性能は安易に捨てられるものではないと思った
以上
#Arduino
#Visual_Studio_Code
#SPI
#ESP
#ESP32
#analog_discovery2
#LSM9DS1