北東北 LabVIEWユーザー会

キャンセル
次の結果を表示 
次の代わりに検索 
もしかして: 

SPI接続のAD変換ボード(ADS8688 8ch 500kSPS)を使ってみる

ESP32のADCはそれなりの性能なので使いやすいAD変換ボードを探していたのですが、ADS8688を使ったものが見つかったので、6000円ぐらいしたのですが購入してみました。

 

ADS8688写真.JPG

 

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」を変更して使用しました。文字列から数値への変換関数を浮動小数点のものに変更します。

パネル.pngダイアグラム.png

以下のファイルを添付しました。

7-3_max30102ChartF.vi

ReadChannel0_02.ino

 

標準的なUNO(USBシリアル)で動かしてから、ESP32(USBシリアル)で動かして、最終的にはESP32のWiFiのTCP/IPでどこまで速く送れるかチャレンジしたいと思います。

 

 

0 件の賞賛
メッセージ1/4
2,448件の閲覧回数

日本LabVIEWユーザー会 有志による 完全無料の電子書籍

「LabVIEW Community Editionでプログラミングを楽しもう」へのリンクです。

http://quatsys.com/labview/1109/lvproraku.jp.html 

 

0 件の賞賛
メッセージ2/4
2,403件の閲覧回数

Arduino UNO互換機でうまくいったので、ESP32(MH-ET LIVE ESP32 MiniKit)に変更してみます。

 

M1111243.JPG

 

ESP32(MH-ET LIVE ESP32 MiniKit)のピンアウトです。

 

MH-ET_LIVE_D1_mini_ESP32_pinout.png

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に変更するだけです。

cs変更.png

これだけで、何のトラブルもなくESP32に変更することができました。

プロット.png

 

次は、割り込みを使った一定間隔のサンプリングですね。何kHzまで行けるか興味があります。

 

 

0 件の賞賛
メッセージ3/4
2,338件の閲覧回数

割り込みを使った一定間隔のAD変換は40kHzまでは大きな変更なくできました。色々無駄を削って150kHzぐらいは見かけ上安定していたので、100kHzが手堅いところのようです。

 

M1181246.JPG

 

SPIのクロック信号とCS信号

F0014TEK.BMPF0015TEK.BMP

 

TCPサーバーからデータを取り出すLabVIEWプログラムは変更ありません。

LabVIEWパネル.png

//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を使ったボードが届いたのでそれも試してみます。

 

 

 

 

0 件の賞賛
メッセージ4/4
2,280件の閲覧回数