ESP8266でSPI通信する

![IMAG3080.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/c3faf4df-b691-22df-52b9-45973038926a.jpeg)
[Arduino core for ESP8266 WiFi chip](https://github.com/esp8266/Arduino)ではしばらくの間、SPIのMode 3(CPOLのActive Low)が使えませんでしたが、最近になって使えるようになりました。ここでは、ESP8266で3軸加速度センサを使ったSPI通信を行います。

# 準備
以下のものを用意します。

- ESP8266 [ESPr Developer（ESP-WROOM-02開発ボード）](https://www.switch-science.com/catalog/2500/)：スイッチサイエンス

![ESPr_Developer.png](https://qiita-image-store.s3.amazonaws.com/0/70925/4c15edf7-bc35-28b9-0e14-e5cc1d56db4d.png)

この開発ボードはESP8266のBootモード(Flash Boot/UART Download)切替を自動で行ってくれます。モジュール単体で買うよりも高く付きますが他にもUSBシリアル通信、3.3V電源、リセット回路機能の搭載を考えるとESP8266の開発ではおすすめです。

- [3軸加速度センサモジュールADXL345](http://akizukidenshi.com/catalog/g/gM-06724/)：秋月電子

![adxl345.png](https://qiita-image-store.s3.amazonaws.com/0/70925/74848bb7-81d7-d480-51c1-dd8c8a740f2b.png)

ADXL345はI2Cでも通信できますが、SPIで通信する場合は先程述べたMode 3による通信制御が必要となります。

# 配線
ESP8266とADXL345を以下のように配線します。
![esp8266_adxl345.png](https://qiita-image-store.s3.amazonaws.com/0/70925/3321a4bf-46f2-157f-dca4-49158256379a.png)

ESP8266のピン配置は以下のようになっています。画像はとてもわかりやすい[ESP-WROOM-02を使ってみる : Eleclog.](http://eleclog.quitsq.com/2015/08/esp-wroom-02.html)さんからお借りましました。
![ESP-WROOM-02_pinout_diagram.png](https://qiita-image-store.s3.amazonaws.com/0/70925/32b85615-649b-c215-3ad2-eb86e8adb707.png)
図はESP8266(ESP-WROOM-02)モジュール単体のピン配置ですが、スイッチサイエンス社のESPr Developerも大体似たような配置になっています。注意すべきは今回のプログラムではCSのピンをIO15ではなくIO5に割り当てている点です。

[Arduino core for ESP8266 WiFi chip](https://github.com/esp8266/Arduino)はデフォルトで上の図のようにピンの機能が割り当てられていますが、ESP8266は元々IOピンに任意の機能を割り当てることが可能となっています。そのためESP8266関連の様々な記事でピン配置が異なっているにもかかわらず動作する場合があるのはここに理由があります。

少し混乱してしまうかもしれませんが、WEBを参考にしてESP8266を開発するときはソースコードを確認しつつ配線のチェックを行うと良いでしょう。

|SPI機能|デフォルトピン |今回設定ピン|ADXL345|
|:---:|:---:|:---:|:---:|
| CLK | IO14 | IO14 | SCL |
| MISO | IO12 | IO12 | SDIO |
| MOSI | IO13 | IO13 | SDA |
| CS | IO15 | IO5 | CS |

また、CSの機能の割り当てをIO15からIO5に変えている理由は、IO15がモード切替の機能を担っているためADXL345を接続したときにモード切替が正常に行われない可能性を回避するためです。(実際接続したところログが正常に出力されずうまく動きませんでした)

# プログラム
以下のプログラムを書き込みます。[202. ADXL345 - Fab蔵 Docs](https://sites.google.com/a/gclue.jp/fab-zang-docs/ni-yinkiiot/202-adxl345)さんのコードを流用させていただきました。32bit CPUのESP8266に合わせて変数の型を変えてあります。書き込みパラメータは以下を参考にしてください。

| 項目 | 値 |
|:-----------|------------:|
| ボード | Generic ESP8266 Module |
| Flash Mode | QIO |
| Flash Frequency | 80MHz |
| CPU Frequency | 80MHz |
| Flash Size | 4M(1M SPIFFS) |
| Debug port | Serial |
| Debug Level | なし |
| Reset Method | nodemcu |
| Upload Speed | 115200 |

```c:ADXL345_SPI.ino
#include <SPI.h>

//ADXL345 レジスタアドレス
#define BW_RATE     0x2C    //Data rate and power mode control
#define POWER_CTL   0x2D    //Power Control Register
#define DATA_FORMAT 0x31    //Data format control
#define DATAX0      0x32    //X-Axis Data 0

#define SS 5 //デフォルトから設定を変更して再定義(IO15 -> IO5)

char values[10];
int16_t x,y,z;
float xg, yg, zg;

void setup() {
  SPI.begin();
  SPI.setDataMode(SPI_MODE3);
  SPI.setBitOrder(MSBFIRST);
  
  Serial.begin(115200);
  
  // SSをHightに
  pinMode(SS, OUTPUT);
  digitalWrite(SS, HIGH);
  
  // ADXL345初期化
  writeRegister(DATA_FORMAT, 0x03); // ±16g 10bit
  writeRegister(POWER_CTL, 0x08);  // 測定モード
}

void loop() {
  // DATAX0レジスタから6バイトを取得
  readRegister(DATAX0, 6, values);
  
  // 2Byteのデータを再構成
  x  = ((int16_t)values[1]<<8)|(int16_t)values[0];
  y  = ((int16_t)values[3]<<8)|(int16_t)values[2];
  z  = ((int16_t)values[5]<<8)|(int16_t)values[4];
   
  // 0.03125 = (16*2)/(2^10)
  xg = x * 0.03125;
  yg = y * 0.03125;
  zg = z * 0.03125;
  
  // ログ出力
  Serial.print(xg);
  Serial.print("\t");
  Serial.print(yg);
  Serial.print("\t");
  Serial.println(zg);
}

void writeRegister(char registerAddress, char value) {
 // SPI開始時にSSをLOWにする
 digitalWrite(SS, LOW);
 // レジスタアドレス送信
 SPI.transfer(registerAddress);
 // レジスタに設定する値送信
 SPI.transfer(value);
 // SPI終了時にCSをHIGHにする
 digitalWrite(SS, HIGH);
}

void readRegister(char registerAddress, int16_t numBytes, char * values) {
 // 書き込みフラグを立てる
 char address = 0x80 | registerAddress;
 // 複数バイトフラグを立てる
 if(numBytes > 1)address = address | 0x40;
 // SPI開始時にSSをLOWにする
 digitalWrite(SS, LOW);
 // 読み出し先レジスタのアドレスを送信
 SPI.transfer(address);
 // 値の読み出し
 for(int16_t i=0; i<numBytes; i++) {
   values[i] = SPI.transfer(0x00);
 }
 // SPI終了時にCSをHIGHにする
 digitalWrite(SS, HIGH);
}
```

# 結果
シリアルモニタで確認すると正常に動作していれば以下のようになります。基板を傾けると値が変化します。ボーレートは115200 bpsにしてください。もし、文字化けする場合は一度リセットボタンを押します。1列目がX軸、2列目がY軸、3列目がZ軸の値です。
![スクリーンショット 2016-08-14 15.02.28.png](https://qiita-image-store.s3.amazonaws.com/0/70925/08ed9ced-305e-3848-79f1-3e70aee0b4cf.png)

# (補足)SPI通信のModeについて
IO14(CLK)ピンの出力波形をオシロスコープで確認してみました。今まではCPOLの制御ができずにMode 1(Mode 2)のActive Highしかできませんでした。

![1426622448.png](https://qiita-image-store.s3.amazonaws.com/0/70925/066cbc51-ecfc-90b4-c559-7b1e9f74eba0.png)
Mode 1

しかし、最近ライブラリが修正されMode 3(Mode 4)を指定すると正常にActive Lowの波形が出力されるようになりました。
![1814333531.png](https://qiita-image-store.s3.amazonaws.com/0/70925/c314a317-976a-239d-ff2c-334812eb1ad8.png)
Mode 3