ESP32 と MCP3008 で SPI の勉強

ESP32 を使って、 SPI の基本的な動作を確認します。
ここで接続するデバイスとして、 10 bit x 8 チャンネルの ADコンバータ MCP3008 が手元にあったのでそれを使いました。

# 環境

- Arduino 1.8.10 for Linux
- esp32 by Espressif System Version 1.0.3
- WEMOS LOLIN32

# 接続


![IMG_20200726_155656044.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/149147f6-78f9-2dbc-de2f-f42ac091c846.jpeg)

![IMG_20200726_155512981.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/291c1c1d-890d-c953-da48-cad8fce980f5.jpeg)

|信号名|MCP3008 PIN|MCP3008 信号名|ケーブル|ESP32 GPIO  | ESP32 信号名|
|:---:|:---:|:---:|:---:|:---:|:---:|
|VDD|16|VDD |オレンジ |3V3|3V3|
||15|VREF |VDD |||
||14|AGND|DGNDに接続|
|SCK|13|CLK |黄色|IO18|VSPCLK|
|MISO|12|DOUT |紫|IO19|VSPIQ|
|MOSI|11|DIN |灰|IO23|VSPID|
|CS|10|CS/SHDN |緑|IO5|VSPICS0|
|GND|9|DGND |黒|GND|GND|

MCP3008 はアナログ入力ポートが CH0 〜 CH7 の 8チャンネルあります。それぞれ 10 bit の分解能で AD 変換を行い、SPIでマイコンなどに取り込むことができます。テストのため、CH0 に 1kΩの抵抗をつけて、VDDにつないでみました。

# MCP3008 の SPI 設定


データシートから、転送に必要な設定を読んでみます。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/2acc8932-1a92-cf03-eec0-50f8084e4314.png)


- MSBFIRST (LSBFIRST転送もMSBFIRST転送の後に行われる)
- アイドリングHIGH/LOW両対応
- 転送タイミング クロックの立ち上がりエッジ (2020/08/23:「立ち下がり」と記述していたのを修正しました)
- クロックスピード 10kHz以上,最高1.35MHz (VDD=2.7V時)

スタートビットとして1ビット送信しますが、SPIライブラリは8ビット単位での送信です。そのために前に7ビットの0を入れて転送します。
それに続く8 bit において以下のようにデータが送受信されます。

|bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0|
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|SGL/DIFF|D2|D1|D0|?|0|B9|B8|

ここで、bit7~bit4はMCP3008への入力です。SGL/DIFFは シングルエンド入力と作動入力のビットです。1ならシングルエンド、0なら作動入力となります。
今回はシングルエンド入力を使います。その場合、D2/D1/D0 はチャンネル指定、CH0なら D2=0,D1=0,D0=0。CH1ならD2=0,D1=0,D0=1。
bit2〜bit0 はMCP3008からの出力です。bit2 が常に0,B9およびB8で10bitのAD変換値のうちの上位2ビットとなります。 


更に続く 8 bit において、以下のようにMCP3008から続くB7〜B0の残り8ビットが出力されます。

|bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0|
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|B7|B6|B5|B4|B3|B2|B1|B0|


この転送が終わってもCS信号がLow出会った場合には、以下の 7bit がMSBFIRST転送として行われます。

||||||||
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|B1|B2|B3|B4|B5|B6|B7|


# SPI の転送モード

|Mode|CPOL(クロック極性)|CPHA(クロック位相)|
|:---:|:---:|:---:|
|0|	0|	0|
|1|	0|	1|
|2|	1|	0|
|3|	1|	1|

クロック極性は 0:アイドルLo パルスHigh 1:アイドルHigh パルスLo
クロック位相は 0:パルス先端で転送  1:パルス後端で転送

なので MCP3008 は SPI の モード 0 または モード 3 を使用することになります。



# ESP32 のSPI設定

ESP32 は VSPI、HSPI の2つの SPIチャンネルがありますが、SPIライブラリだとVSPIの方が使われるそうです。
更に、 SPI のポート割当は変更可能なようですが、今回はデフォルトの設定を使っています。
速度は SPISettings パラメータで設定します。 今回は 1000000 と設定することで、最大 1MHz で駆動することになります。

# コード

```


#include <SPI.h>
#define SS 5
float Vref = 3.3 ;
 
SPISettings settings(1000000,MSBFIRST,SPI_MODE1);


void setup() {
    pinMode(SS, OUTPUT);
    Serial.begin(115200);
    SPI.begin();
}
   
void loop(){
    byte single = 1 << 7; // Single...1 /Diff...0
    byte channel = 0;
    byte buffer1  = single |  ( channel << 4 ) ;
    byte buffer2 = 0 ;
    float volt; 
    unsigned int getdata;
    SPI.beginTransaction(settings);
    digitalWrite(SS, LOW);
    SPI.transfer( 0b00000001); // Start bit 
    buffer1 = SPI.transfer(buffer1); // CH0 singleEnd
    buffer2 = SPI.transfer(buffer2);
    digitalWrite(SS, HIGH);
    SPI.endTransaction();
    getdata = ((buffer1 & 0x03) *256) + buffer2;
    volt = getdata*Vref /1024;
    Serial.print(   getdata ); 
    Serial.println( ": " +String(volt,3) + "V");
    delay(1000);
    delay(1000);
}
```


# 実行結果

```
1023: 3.297V
1023: 3.297V
1023: 3.297V
7: 0.023V
23: 0.074V
0: 0.000V
0: 0.000V
0: 0.000V
154: 0.496V
1023: 3.297V
1023: 3.297V
1023: 3.297V
1023: 3.297V
1023: 3.297V
1023: 3.297V
・
・
・

```


CH0 に接続した抵抗を、VDDからGNDに繋ぎ変え、再度VDDに接続した時の値です。