動かして理解する！SPI通信の仕様(BMP280を前提に)

# はじめに
SPI通信対応デバイスを初めて触って、理解するのにだいぶ苦しんだので自分用メモとして残すものとする。

デバイス：BMP280 https://docs.rs-online.com/f7b2/0900766b81378bee.pdf
マイコン：Arduino Due

# 1. SPI通信を用いたデータ書き込み
BMP280の仕様書より。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/ea75fc51-a06d-436a-bfcb-9f51cb6ba9d5.png)

マイコンからCSB(チップセレクト)のON→OFFで対象デバイスへの送る受信をスタート。
その後、SCK(シリアルクロック)に8回ON⇔OFF信号が入力されるのと同期してSDI(シリアル・データイン)に8bitの情報がON/OFF信号としてデバイスに入力されます。SDO(シリアル・データアウト)はデバイスの情報をマイコン受信する際に使用されます。

SDIは１つの線を使ってコントロールバイト(制御用信号)とデータバイト(データ信号)の両方をデバイスに送信します。下記は２つのアドレスにデータバイトを送信する場合のビットパターン。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/9d7fd3e5-6ab5-419f-814d-c4ddffc11f73.png)

ここで注目するのはコントロールバイトの初期１ビットで、BMP280の仕様では「0」でWrite(書き込み)、「1」でRead(読み込み)として設定します。
（上記は２つのアドレスにデータバイトを送信する場合のため、初期1ビットは両方とも「0」が設定されています）


実際に１つのアドレスにデータバイトを送信した際のオシロスコープ観測波形は下記。黄色がクロック、水色がチップセレクト、緑が送信データです。ピンクは受信データのため、一切反応しない結果になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/f8d861a1-d3bc-4050-9795-b4a5b5c0a6bb.png)

arduinoコード
```c
digitalWrite(SS0PIN, HIGH); // チップセレクト無効
SPI.beginTransaction(spiA); // データ送信開始
digitalWrite(SS0PIN, LOW); // チップセレクト有効
SPI.transfer(0x74); // コントロールバイト 初期1ビットは「0」
SPI.transfer(0b00100111); // データバイト送信
digitalWrite(SS0PIN, HIGH); // チップセレクト無効
```

# 2. SPI通信を用いたデータ読み込み

コントロールバイトの初期１ビットに「1」＝Read(読み込み)を設定することでデータ読み込みが可能です。なお、BMP280は一度データ読み込みモードに設定すると以降はアドレスが加算されながら連続でデータ受信が行われます。（下記はコントロールバイトでF6hをアドレス指定、次の1バイトでF6hのデータが、さらに次の１バイトでF7hのデータが読み込まれる例を示しています）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/6c066b3b-ed54-4d17-aa1f-be5a2d7187d5.png)

実際に３つのアドレスのデータを連続受信した際のオシロスコープ観測波形は下記。黄色がクロック、水色がチップセレクト、緑が送信データ、ピンクが受信データになります。送信データは最初１バイト分のみアドレス情報が送信され、以降は何も送信していません。（クロックがOFFのときはONになる仕様のようです）受信データは２つ目、３つ目、４つ目のバイトで受信が行われます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/02bec195-00b6-4cf1-946b-4f1ec917e88f.png)

なお、一度データ受信モードに設定された後にデータ送信モードに設定したりは出来ないようです。（チップセレクトを再度無効→有効に設定すれば可能）

arduinoコード
```c
digitalWrite(SS0PIN, HIGH); // チップセレクト無効
digitalWrite(SS0PIN, LOW); // チップセレクト有効
SPI.transfer(0xFA); // コントロールバイト 初期1ビットは「1」
data1 = SPI.transfer(0); // データバイト受信(アドレス：FA)
data2 = SPI.transfer(0); // データバイト受信(アドレス：FB)
data3 = SPI.transfer(0); // データバイト受信(アドレス：FC)
digitalWrite(SS0PIN, HIGH); // チップセレクト無効
```

`SPI.transfer(0)` はデータ受信関数になっていますが、やっていることはSDI(シリアル・データイン)に無効データを送りつつSDO(シリアル・データアウト)からデータを受信している…と勝手に理解しています。



# 3.BMP280で温度データを受信
下記、パワーモードをノーマルモードに設定→温度データを3バイト連続受信するテストコードになります。パワーモードの設定は別に無くても温度は読めましたが、データ書き込みのサンプルとして残しています。

パワーモード設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/b4a6d70b-0d68-42ec-b9ec-7dccae5e8977.png)
アドレスはF4とありますが、書き込みのため先頭１ビットを「1」→「0」に置き換えた74をコードでは指定しています。

温度・気圧データ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/8a334de4-4d01-4e78-82db-9e3796e48e33.png)
FC、FB、FAの３バイトが温度データになります。（全20ビットのデータを先頭4ビット+8ビット+8ビットに割り付けている）
データ受信は連続受信可能なため、コントロールバイトはFAだけを指定します。

arduinoコード
```c
#include <SPI.h>

#define SS0PIN (10)

SPISettings spiA(2000000, MSBFIRST, SPI_MODE0); 
unsigned char data1 = 0;
unsigned char data2 = 0;
unsigned char data3 = 0;

void setup() {
  pinMode(SS0PIN, OUTPUT);
}

void loop() {
  digitalWrite(SS0PIN, HIGH);
  SPI.begin();
  SPI.beginTransaction(spiA);

  // パワーモードをNormalに設定
  digitalWrite(SS0PIN, HIGH); // チップセレクト無効
  SPI.beginTransaction(spiA); // データ送信開始
  digitalWrite(SS0PIN, LOW); // チップセレクト有効
  SPI.transfer(0x74); // コントロールバイト 初期1ビットは「0」
  SPI.transfer(0b00100111); // データバイト送信

  // 温度データを連続受信
  digitalWrite(SS0PIN, HIGH); // チップセレクト無効
  digitalWrite(SS0PIN, LOW); // チップセレクト有効
  SPI.transfer(0xFA); // コントロールバイト 初期1ビットは「1」
  data1 = SPI.transfer(0); // データバイト受信(アドレス：FA) msb
  data2 = SPI.transfer(0); // データバイト受信(アドレス：FB) lsb
  data3 = SPI.transfer(0); // データバイト受信(アドレス：FC) xlsb
  digitalWrite(SS0PIN, HIGH); // チップセレクト無効

  SPI.endTransaction();
}
```

温度センサに指を当てる→離したときの計測データ下記になります。
data1がmsb、data2がlsbのため、下位データであるlsbがカウントアップした後にmsbがカウントアップする挙動が見て取れます。xlsbはlsbよりさらに下位のデータと思いますが、高分解能設定していないせい（だと思う、たぶん）か0固定になりますが、今回はSPI通信の理解が目的なのでここまでにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/4637d74e-c6ee-4df8-b030-2d1606b3cf91.png)

# おわりに
SPI通信の知識が無さ過ぎて、ここまで動かすのに２日～３日かかりました。久々に疲れた…
けれども、実際にオシロの動きを見ながら仕様を理解するのは楽しかったです。