こんにちは、shotaです。
社員研修として、オリジナルマウスの製作を始めます。
[前回の記事]ではオリジナルマウスの仕様を決めました。
今回はタイトルの通り、ESP32で9軸センサMPU9250とSPI通信をします。
確認すること
- ESP32でSPI通信できるか
- [好きなピンをSPIに割り当てられる]のは本当なのか?
できあがったプログラムがこちらです
n分クッキングみたいですが、先に完成したプログラムと通信結果を紹介します。
プログラムは[GitHub]に公開しています。[ESP-IDFのサンプル]をもとに作成しました。
WHO_AM_I(0x75)の応答のみを実装しています。
通信結果はこちらです。MPU9250からの返答0x71が確認できます。
$ make monitor ~~~ 省略 ~~~ I (267) cpu_start: Pro cpu start user code I (285) cpu_start: Starting scheduler on PRO CPU. I (0) cpu_start: Starting scheduler on APP CPU. MPU ID: 71
!
SPI通信信号をロジアナで確認した結果がこちらです。
ESP32からの送信信号0xF5(Readビット(0x80) + WHO_AM_I(0x75))と、MPU9250からの返答0x71が確認できます。
また、通信クロック500 kHzも確認できます。
それでは、このプログラムができるまでの過程をここに書きます。
開発環境の導入からHello Worldまで
は、省略します。公式ページを見れば実施できるため、ここには書きません。
[ESP-IDF Programming Guide]のGet Startedから開発環境(ESP-IDF)を導入してください。
私は、Linux(Xubuntu 16.04)に開発環境を作りました。使用するESP32のボードは[ESP32-DevKitC-32D]です。
ESP32のSPI機能について確認
まず、[公式ドキュメント]や、[Technical Reference Manual]、[データシート]を閲覧することを推奨します。
ここでは、引っかかった項目をピックアップします。
4つのSPIモジュールがある (が、2つしか使えない)
SPI0、SPI1、VSPI、HSPIがありますが、SPI0とSPI1は内部のフラッシュメモリに接続されているため使用できません。
今回のプログラムではHSPIを使用しました。
8bit以外のデータを送るとき要注意
[公式ドキュメントのTips]にあるTransactions with integers other than uint8_tを参照してください。
ESP32のSPI通信では、1バイトずつMSB firstモードで送信されます。
例えば、送信バッファに0x15 (0b0000 0101) が格納されてる時、8bit サイズで送信すると 0 0 0 0 0 1 0 1 という順番でデータが送信されます。
ところが、このデータを5bit サイズで送信すると、 0 0 0 1 0 が送信されます。 0b00101 ではないのでご注意ください。
基本8bitサイズでデータを送るので、この問題には直面しないと思います。
しかし、16 bit ( あるいは32bit) のデータを送信するときは更に注意です。
ESP32は[リトルエンディアン]で設計されています。 例えば、送信バッファに0xABCD という16bitのデータを格納した時、メモリ上ではCD ABの順番で保存されます。
そのため、0xABCD を送信したつもりでも、実際はC D A B の順番で送信されています。
この対策として、データの順序を変換する[SPI_SWAP_DATA_TX]と、[SPI_SWAP_DATA_RX]というマクロが用意されています。
実際に使用した例がこちらです。(GitHubのコードと同じです)
uint16_t tx_data = (address | READ_FLAG) << 8; tx_data = SPI_SWAP_DATA_TX(tx_data , 16); t.tx_buffer = &tx_data; ret=spi_device_polling_transmit(spi, &t); // Transmit! assert(ret==ESP_OK); // Should have had no issues. uint8_t data = SPI_SWAP_DATA_RX(*(uint16_t*)t.rx_data, 16) & 0x00FF; // FF + Data return data;
まず16bitの領域(tx_data)に 0xF500を格納します。 0xF5は、WHO_AM_Iのアドレス0x75 + Readビット0x80です。
このままだと、メモリ上では00 F5という順番で格納されているので、送信の順番も00 F5になってしまいます。
これを、SPI_SWAP_DATA_TXをつかって変換します。変換後は、 F5 00 という順番でメモリに格納されます。
受信データも同様に変換します。センサモジュールからはFF 71 という順番で応答が返ってきますが、受信バッファには 71 FFという順番で格納されます。
これを、SPI_SWAP_DATA_RXを使って、FF 71 の順番に変換します。
GPIOマトリックスとIOMUX
[GPIO matrix and IOMUX]に書かれているように、GPIOマトリックスを使用すると、40MHz以上で通信できません。高速通信をしたい場合はIOMUXピンを使用してください。
そもそも、GPIOマトリックスとIOMUXとは何なのか?については、[こちらの記事]が参考になりました。
GPIOマトリックスによって好きなピンに機能を割り当てられますが、通信速度の制限がかかります。
今回はIOMUXピンでSPI通信を確認した後、IOMUX以外のピンを使ってみます。
MPU9250とESP32の接続
9軸センサモジュール MPU9250は[こちらのモジュール]を使います。
オリジナルマウスで使用予定の[ICM-20648]とピン互換であるため、職場の先輩からお借りしています。
※MPU9250は[新規設計非推奨]です。ご注意ください。
[MPU9250のデータシート]を参考に、ブレッドボード上で配線しました。
MPU9250ピン | ESP32ピン | ESP32機能 |
VCC | 5V | 5V |
GND | GND | GND |
AD0 / SDO | 12 | MISO |
SDA / SDI | 13 | MOSI |
SCL / SCLK | 14 | CLK |
nCS | 15 | CS |
プログラムの作成
プログラムの全体は[GitHub]のコードを参照してください。
ポイントは以下のとおりです。
- MPU9250のSPIクロックは最大1MHz。今回は500kHzを設定する。
- MPU9250のSPIモードは3。[SPIモードとは]
- MPU9250のSPI通信は16クロックサイクル(以上)で行われる。前半8サイクルでアドレス+R/Wビットを送信し、後半8サイクルでデータを送信する。
通信結果は記事の最初のとおりです。
IOMUXピン以外を使ってみる
ピンの割当を以下のように変更します。これらのピンはHSPIのIOMUXピンではありません。
MPU9250ピン | ESP32ピン | ESP32機能 |
VCC | 5V | 5V |
GND | GND | GND |
AD0 / SDO | 16 | MISO |
SDA / SDI | 17 | MOSI |
SCL / SCLK | 18 | CLK |
nCS | 19 | CS |
ピン割り当て以外に、通信速度も500kHzから1MHzに変更します。GPIOマトリックスを使用しても、40MHzまでは速度が出せるので通信できるはずです。
プログラムは以下のように変更しました。
#define PIN_NUM_MISO 16 // 12 #define PIN_NUM_MOSI 17 // 13 #define PIN_NUM_CLK 18 // 14 #define PIN_NUM_CS 19 // 15 /* ----- 省略 ----- */ spi_device_interface_config_t devcfg={ .clock_speed_hz=1000*1000, //Clock out at 1 MHz .mode=3, //SPI mode 3 .spics_io_num=PIN_NUM_CS, //CS pin .queue_size=7, //We want to be able to queue 7 transactions at a time };
通信結果がこちらです。MPU9250からの返答0x71が確認できます。
$ make monitor ~~~ 省略 ~~~ I (267) cpu_start: Pro cpu start user code I (285) cpu_start: Starting scheduler on PRO CPU. I (0) cpu_start: Starting scheduler on APP CPU. MPU ID: 71
SPI通信信号をロジアナで確認した結果がこちらです。
ESP32からの送信信号0xF5(Readビット(0x80) + WHO_AM_I(0x75))と、MPU9250からの返答0x71が確認できます。
また、通信クロック1 MHzも確認できます。
今回のまとめと、次回やること
今回のまとめです。
- ESP32でSPI通信できました
- 好きなピンをSPIに割り当てられました
次回こそは、マウスの設計方針を書きます。