Edited at

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