01-09-2023 12:40 AM
ESP32のADCはそれなりの性能なので使いやすいAD変換ボードを探していたのですが、ADS8688を使ったものが見つかったので、6000円ぐらいしたのですが購入してみました。
AliExpressで購入したのですが、Ebayでもさらに少し安く売っていました。基板の説明が英語なのでこちらをリンクします。
https://www.ebay.com/itm/184906269558
TI社のADS8688を紹介するキーワードは「バイポーラ入力範囲に対応、16 ビット、500kSPS、8 チャネル、単一電源電圧、SAR ADC」でデータシートは以下からダウンロードできます。
https://www.ti.com/product/ja-jp/ADS8688
たいへんありがたいことにArduinoのライブラリがあります。
https://github.com/siteswapjuggler/ADS8688a
サンプルプログラムも付いています。
8チャンネル用のread_all_channels.inoはそのままで動きましたが、チャンネル0用のread_channel.inoは少し変更が必要でした。
AD変換ボードとArduino UNOとの接続は以下のように標準的なものです。基準電圧はADS8688内部でつくられるので、V+に5Vを接続すればOKです。
* CS: ------- pin 10
* SDI: ------ pin 11
* SDO: ---- pin 12
* SCK: ----- pin 13
* GND: ----- GND
* V+: -------- 5V
ライブラリを使って動かしながら、ADS8688のデータシートを読み進めるのがお勧めです。
ライブラリは色々な使い方に対応させるために複雑化していますので、自分が使う機能だけ取り出してしまうと見通しが良くなります。好みの問題かもしれませんが、、。
/* ADS8688 library example
*
* PIN CONNECTION:
* GNF: GND
* REFIO: NC (output the 4.096V reference when REFSEL is connected to GND)
* REFSEL: GND (enable internal 4.096V reference)
* SDI: pin 11 / MEGA pin 51
* RST/PD: +5V
* DAISY: NC
* CS: pin 10 / MEGA pin 10
* SCK: pin 13 / MEGA pin 52
* SDO: pin 12 / MEGA pin 50
* ALARM: NC
* AVDD: +5V
* DVDD: +5V (Digital Logic Level)
*/
//Special thnks to ADS8688 library
//Modified to the skeleton of the program by Koji Ohashi
#include <SPI.h>
#define NO_OP 0x00
#define AUTO_RST 0xA0
#define AUTO_SEQ_EN 0x01
#define CH_PWR_DN 0x02
#define RG_Ch_0 0x05 // Channel 0 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_1 0x06 // Channel 1 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_2 0x07 // Channel 2 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_3 0x08 // Channel 3 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_4 0x09 // Channel 4 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_5 0x0A // Channel 5 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_6 0x0B // Channel 6 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_7 0x0C // Channel 7 Input Range: default 0x00 - bit 3-0 to select range
#define R0 0x00 // Input range to -2.5/+2.5*Vref +/- 10.24V (Vref=4.096V)
#define R1 0x01 // Input range to -1.25/+1.25*Vref +/- 5.12V (Vref=4.096V)
#define R2 0x02 // Input range to -0.625/+0.625*Vref +/- 2.56V (Vref=4.096V)
#define R5 0x05 // Input range to +2.5*Vref 10.24V (Vref=4.096V)
#define R6 0x06 // Input range to +1.25*Vref 5.12V (Vref=4.096V)
#define CS 10
void setup() {
pinMode(CS,OUTPUT);
digitalWrite(CS,HIGH);
SPI.begin();
writeRegister(AUTO_SEQ_EN,0x00000001);//Enable ch0
writeRegister(CH_PWR_DN,~0x00000001);//Disable other chs
writeRegister(RG_Ch_0,R6);//range 0-5.12V
cmdRegister(AUTO_RST);//Auto mode
Serial.begin(115200);
}
void loop() {
uint16_t val = cmdRegister(NO_OP);//get data
Serial.println(val*5.12/65535);
delay(10);
}
void writeRegister(uint8_t reg, uint8_t val) {
SPI.beginTransaction(SPISettings(17000000, MSBFIRST, SPI_MODE1));
digitalWrite(CS, LOW);
SPI.transfer((reg << 1) | 0x01);//ADDR[6:0],WR/RD[1]
SPI.transfer(val);//DATA[7:0]
SPI.transfer(0x00);//READBACK cycles
digitalWrite(CS, HIGH);
SPI.endTransaction();
}
uint16_t cmdRegister(uint8_t reg) {
SPI.beginTransaction(SPISettings(17000000, MSBFIRST, SPI_MODE1));
digitalWrite(CS, LOW);
SPI.transfer(reg);
SPI.transfer(0x00);
byte MSB = SPI.transfer(0x00);
byte LSB = SPI.transfer(0x00);
uint16_t result = ( MSB << 😎 | LSB;
digitalWrite(CS, HIGH);
SPI.endTransaction();
return result;
}
受け取るLabVIEWプログラムは無料の電子ブック「LabVIEW Community Editionでプログラミングを楽しもう」の第7章のサンプルVI「7-3_max30102Chart.vi」を変更して使用しました。文字列から数値への変換関数を浮動小数点のものに変更します。
以下のファイルを添付しました。
7-3_max30102ChartF.vi
ReadChannel0_02.ino
標準的なUNO(USBシリアル)で動かしてから、ESP32(USBシリアル)で動かして、最終的にはESP32のWiFiのTCP/IPでどこまで速く送れるかチャレンジしたいと思います。
01-09-2023 03:23 AM
日本LabVIEWユーザー会 有志による 完全無料の電子書籍
「LabVIEW Community Editionでプログラミングを楽しもう」へのリンクです。
http://quatsys.com/labview/1109/lvproraku.jp.html
01-11-2023 12:45 AM
Arduino UNO互換機でうまくいったので、ESP32(MH-ET LIVE ESP32 MiniKit)に変更してみます。
ESP32(MH-ET LIVE ESP32 MiniKit)のピンアウトです。
SPIピンは並んでいます。
SCK---IO18
MISO---IO19
MOSI---IO23
CS0---IO5
配線は
ADS8688---- ESP32
5V---- VCC
GND ---- GND
CLK---- IO18
SDO---- IO19
SDI---- IO23
CS---- IO5
となります。
ArduinoプログラムはCSピンの数字を10から5に変更するだけです。
これだけで、何のトラブルもなくESP32に変更することができました。
次は、割り込みを使った一定間隔のサンプリングですね。何kHzまで行けるか興味があります。
01-18-2023 12:46 AM
割り込みを使った一定間隔のAD変換は40kHzまでは大きな変更なくできました。色々無駄を削って150kHzぐらいは見かけ上安定していたので、100kHzが手堅いところのようです。
SPIのクロック信号とCS信号
TCPサーバーからデータを取り出すLabVIEWプログラムは変更ありません。
//esp32 board manager ver2.0.6
//Arduino ide 1.8.19
/*ADS8688: - MH ET LIVE ESP32 MiniKit
* CS: ------ pin 5
* SDI: ----- pin 23
* SDO: ----- pin 19
* SCK: ----- pin 18
* GND: ----- GND
* V+: ------ VCC(5V)
*/
//Special thanks to ADS8688 library
//Modified by Koji Ohashi
#include <SPI.h>
#define NO_OP 0x00
#define AUTO_RST 0xA0
#define AUTO_SEQ_EN 0x01
#define CH_PWR_DN 0x02
#define RG_Ch_0 0x05 // Channel 0 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_1 0x06 // Channel 1 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_2 0x07 // Channel 2 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_3 0x08 // Channel 3 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_4 0x09 // Channel 4 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_5 0x0A // Channel 5 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_6 0x0B // Channel 6 Input Range: default 0x00 - bit 3-0 to select range
#define RG_Ch_7 0x0C // Channel 7 Input Range: default 0x00 - bit 3-0 to select range
#define R0 0x00 // Input range to -2.5/+2.5*Vref +/- 10.24V (Vref=4.096V)
#define R1 0x01 // Input range to -1.25/+1.25*Vref +/- 5.12V (Vref=4.096V)
#define R2 0x02 // Input range to -0.625/+0.625*Vref +/- 2.56V (Vref=4.096V)
#define R5 0x05 // Input range to +2.5*Vref 10.24V (Vref=4.096V)
#define R6 0x06 // Input range to +1.25*Vref 5.12V (Vref=4.096V)
#define SCK 18
#define MISO 19
#define MOSI 23
#define CS 5
//TCP data server:Special thanks to
//https://tsunelab-programming.com/esp32-socket
//https://lang-ship.com/blog/work/esp32-tcp-ip-socket/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#include <ESPmDNS.h>
const IPAddress ip(192,168,10,30);
const IPAddress subnet(255,255,255,0);
WiFiServer server(5000);
#define CMND_SIZE 256
byte myCmnd[CMND_SIZE];
#define DATA_SIZE 4096
volatile int16_t dataArray[DATA_SIZE];
byte dataArrayByte[DATA_SIZE*2];
unsigned long no_op_Data32 = 0x00000000;
unsigned long ADC_Data32 = 0x00000000;
volatile int read_count=0;
volatile int16_t sensorValue=0;
//TrigAI
#define LEDC_CHANNEL_TrigAI 4 //channel max 15//CHANNEL_4とCHANNEL_5は周波数とタイマービットを同一値
#define LEDC_TIMER_BIT_TrigAI 6 //64(0-63)
#define LEDC_BASE_FREQ_TrigAI 100000.0// 100kHz
//#define LEDC_BASE_FREQ_TrigAI 150000.0// 150kHz max Frequency
#define DUTY_TrigAI 8//50% 0x20 = 32
#define GPIO_PIN_TrigAI 26 //GPIO #36~#39 は設定不可
//割り込み関数***************
void IRAM_ATTR readAI(){
dataArray[read_count]=cmdRegister32();//get data
read_count++;
}
//割り込み関数***************
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAP("", "");
delay(100);
WiFi.softAPConfig(ip,ip,subnet);
IPAddress myIP = WiFi.softAPIP();
server.begin();
MDNS.begin("MaDALab");
ledcSetup(LEDC_CHANNEL_TrigAI, LEDC_BASE_FREQ_TrigAI, LEDC_TIMER_BIT_TrigAI);
ledcAttachPin(GPIO_PIN_TrigAI, LEDC_CHANNEL_TrigAI);
ledcWrite(LEDC_CHANNEL_TrigAI, DUTY_TrigAI);
SPI.begin(SCK, MISO, MOSI, SS);
SPI.setHwCs(true); //Hardware control Cs pin
writeRegister(AUTO_SEQ_EN,0x00000001);//Enable ch0
writeRegister(CH_PWR_DN,~0x00000001);//Disable other chs
writeRegister(RG_Ch_0,R6);//range 0-5.12V
cmdRegister(AUTO_RST);//Auto mode
}
void loop() {
int time0,time1;
ReadData();
setDataArrayByte();
WiFiClient client = server.available();
if (client) {
Serial.println("New Client.");
String currentLine = "";
while (client.connected()) {
int available_count = client.available();
if (available_count) {
client.read(myCmnd,available_count);
Serial.write(myCmnd,available_count);
Serial.println("");
setDataArrayByte();
time0=millis();
client.write(dataArrayByte,DATA_SIZE*2);
time1=millis();
Serial.print(time1-time0);
Serial.println(" msec taken to send all data.");
}
}
client.stop();
Serial.println("Client Disconnected.");
}
delay(1000);
}
void ReadData(){
read_count=0;
SPI.beginTransaction(SPISettings(16000000, MSBFIRST, SPI_MODE1));
attachInterrupt(GPIO_PIN_TrigAI, readAI, RISING);//SOの読み取りタイミング
while(read_count<DATA_SIZE){} //全画素取り込み
detachInterrupt(GPIO_PIN_TrigAI);//SOの読み取り終了
SPI.endTransaction();
}
void printData(){
for(int i=0; i<DATA_SIZE; i++){
Serial.println(dataArray[i]);
}
}
void setDataArrayByte(){
for(int i=0; i<DATA_SIZE; i++){
int16_t myData=dataArray[i];
dataArrayByte[i*2]= myData >> 8;
dataArrayByte[i*2+1]= myData & 0x00ff;
}
}
void writeRegister(uint8_t reg, uint8_t val) {
SPI.beginTransaction(SPISettings(16000000, MSBFIRST, SPI_MODE1));
//digitalWrite(CS, LOW);
SPI.transfer((reg << 1) | 0x01);//ADDR[6:0],WR/RD[1]
SPI.transfer(val);//DATA[7:0]
SPI.transfer(0x00);//READBACK cycles
SPI.endTransaction();
}
uint16_t cmdRegister(uint8_t reg) {
SPI.beginTransaction(SPISettings(16000000, MSBFIRST, SPI_MODE1));
SPI.transfer(reg);
SPI.transfer(0x00);
byte MSB = SPI.transfer(0x00);
byte LSB = SPI.transfer(0x00);
uint16_t result = ( MSB << 😎 | LSB;
SPI.endTransaction();
return result;
}
uint16_t cmdRegister32() {
ADC_Data32 = SPI.transfer32(no_op_Data32);
uint16_t result = (uint16_t)ADC_Data32;
return result;
}
Arduinoプログラムを添付します。
ADS8688よりも安価なAD7606を使ったボードが届いたのでそれも試してみます。