Arduino
ESP8266

ESP8266でSPI通信する

More than 1 year has passed since last update.

IMAG3080.jpg
Arduino core for ESP8266 WiFi chipではしばらくの間、SPIのMode 3(CPOLのActive Low)が使えませんでしたが、最近になって使えるようになりました。ここでは、ESP8266で3軸加速度センサを使ったSPI通信を行います。

準備

以下のものを用意します。

ESPr_Developer.png

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

adxl345.png

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

配線

ESP8266とADXL345を以下のように配線します。
esp8266_adxl345.png

ESP8266のピン配置は以下のようになっています。画像はとてもわかりやすいESP-WROOM-02を使ってみる : Eleclog.さんからお借りましました。
ESP-WROOM-02_pinout_diagram.png
図はESP8266(ESP-WROOM-02)モジュール単体のピン配置ですが、スイッチサイエンス社のESPr Developerも大体似たような配置になっています。注意すべきは今回のプログラムではCSのピンをIO15ではなくIO5に割り当てている点です。Arduino core for ESP8266 WiFi chipはデフォルトで上の図のようにピンの機能が割り当てられていますが、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さんのコードを流用させていただきました。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
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

(補足)SPI通信のModeについて

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

1426622448.png
Mode 1

しかし、最近ライブラリが修正されMode 3(Mode 4)を指定すると正常にActive Lowの波形が出力されるようになりました。
1814333531.png
Mode 3