#ESP32のSPI接続でMCP3208からAD変換実験
##初めてQiitaへ投稿
初めて投稿させて頂きます。既出極まりない内容ですが、自分用のメモとしての初投稿です。ご容赦願います。
##ESP32のSPI設定
HSPIを使用し、端子設定はごく一般的なSPI設定です。
- CLK 32
- MISO 33
- MOSI 25
- SS 26 (chip select) Low Active
SPIクロック:4MHzで設定
SPIモード:Mode0 : POL=0,PHA=0 (0,0)モード・・・データを立ち上がりエッジでサンプリング、立下りでシフトするモード
AD終了後、即時次のADを呼んだ時にどのくらいのレートでESP32から読めるのかを確認してみました。また、その時の波形も併せて載せておきます。
#include <SPI.h>
#define SPI1_CLK 32
#define SPI1_MISO 33
#define SPI1_MOSI 25
#define SPI1_SS 26
#define SPI_CLK 4000000
SPIClass SPI1(HSPI);
SPISettings spiSettings = SPISettings(SPI_CLK, SPI_MSBFIRST, SPI_MODE0);//0,0
void setup() {
Serial.begin(115200);
SPI1.begin(SPI1_CLK, SPI1_MISO, SPI1_MOSI, SPI1_SS);
pinMode(SPI1_SS, OUTPUT);
pinMode(SPI1_CLK, OUTPUT);
pinMode(SPI1_MISO, INPUT);
pinMode(SPI1_MOSI, OUTPUT);
digitalWrite(SPI1_SS, HIGH) ;
SPI1.beginTransaction(spiSettings);
}
void loop() {
byte channel = 0;
Serial.println(adc_read(0));
//delay(1);
}
int adc_read(uint8_t channel) {
digitalWrite(SPI1_SS, LOW);
SPI1.transfer(0x06 | channel>>2);//bit7-3:dummy, bit2:start, bit1:single,bit0:D2
uint8_t dh = SPI1.transfer((channel & 0x03) << 6);//D1,D0,X,Null,B11-B8
uint8_t dl = SPI1.transfer(0x00);//B7-B0
digitalWrite(SPI1_SS, HIGH);
return ((dh & 0xF) << 8 | dl); //get 12bits data
}
##MCP3208へのアナログ入力
アナログ入力としては、可変抵抗で 0Vから3.3Vまで振った場合、デジタル出力は12ビットなので、0~4095になります。ただし、0V近辺および3.3V近辺では既にクリッピングされた感じでした。正確には測定していません。
##波形1 クロックとMISOの波形
MCP3208のデータシートでは、5bitでチャンネル指定、1bitのダミーと1bitのNullのあと、12bitのAD出力データがMSB firstで通信される事になるため、全部で19bitのクロックが必要になります。(下記仕様の波形から、SPIモードは 0,0と確認)
ただ、ESP32の標準SPIライブラリでは、バイト単位での送受信に限られるため、頭に5bitのダミーのデータ(0)を入れて、全部で24bit(3バイト)にして通信することになります。19bitで送信する方法は調査不足かもしれません。
(ちなみに、ESP8266では、通常のSPIライブラリでは無いライブラリを用いて、19bitで通信することは出来ました。そのライブラリをESP32で試しては居ません。https://github.com/MetalPhreak/ESP8266_SPI_Driver 中のspi_transaction()関数を利用)
6bit目にStartビットとして'H'を送るまでのクロックは無視されるので、通信的には問題なしです。
MOSIの1バイト目で、Start bitと、Single指定と、Channel番号のD2を指定しています。Channel番号の下位ビットは2バイト目のbit7,bit6で送っています。データ送信と受信はSPIの場合は同時です。。
下記が3バイト(24bit)のCLKと、MISO信号です。2バイト目のbit3-0と3バイト目のb7-0がAD変換結果の12bitデータとなります。
##波形2 Delay無しで回した時の サンプリング周期の確認
AD終了後、すぐ次のループに入る処理を行った場合、サンプリング間の周期は、約520usec(サンプリング周期は 1.9kHz)になりました。Delay無しで繰り返しているにも関わらず、以前ESP8266で取った時の272us(Serial.printlnあり)より遅い理由は分かりません。クロック速度はESP8266の時も 4MHzでしたのでクロックの差は無し。Dummyのクロック5発分は、ESP8266の時には不要だったので、今回の方が少し不利ですが、それ以上の差がある。ライブラリの違いかもしれません。
ちなみに、Serial.printlnを止めれば、短縮されますが、波形を取り忘れました(汗)
##Arduino IDEのシリアルプロッタでのAD出力確認
少しDelayを入れて(波形を取りやすくするために10msを入れました)、 Analog入力を0V~3.3Vの可変VRを動かして、AD結果を波形にしたものがこれです。電圧が0手前、3.3V手前の時に、AD結果として既にクリップされていますが、正確な電圧範囲は測定していません。
とりあえず、Qiita初投稿はここまでです。
次はこの値を Bluetoothで送り、別のESP32で受信した値でサーボモーターを回す予定です。