北東北 LabVIEWユーザー会

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

SPI接続のAD変換ボード(AD7606 16bit 8ch 200kSPS*)を使ってみる

ADS8688を注文した後で、1500円ぐらいのAD変換ボードが見つかりました。AD7606を使ったボードはパラレル出力のものとSPI出力のものがあります。抵抗を付け替えればどちらでも利用できますが、難儀なことは避けてSPIのものを探しました。

 

M1241256.JPG

 

AD7606を使いながらデータシートを読み進めると8ch同時サンプリングです。詳しくはANALOG DEVICESのデータシートをご覧ください。

全チャンネルに1kHzのノコギリ波を入れてデータを見ると揃っています。

8chサンプリング.png

Teensy用のArduinoプログラム があったので、ESP32用に移植しました。(MH ET LIVE ESP32MiniKit)

 

/*
    AD7606 ESP32 SPI
    
    D7(MISO)--IO19
    BUSY--IO35
    CS--IO5
    RD(CLK)--IO18
    RST--IO34
    CB,CA--IO33
    RANGE--GND(+/-5V)
    OS0,OS1,OS2--GND(No Over Sampling)
    GND--GND
    5V--5V
*/
//Special Thanks to 
//https://forum.pjrc.com/threads/64097-Teensy-3-2-with-AD7606

#include <SPI.h>

uint32_t start, stop;

#define SCALE_FACTOR 0.000152587890625
#define BUSY 35
#define RESET 34
#define START_CONVERSION 33
#define CHIP_SELECT 5
#define MISO 19
#define MOSI 18
#define LED 2
#define TOTAL_RAW_BYTES 16

int bytesToRead = TOTAL_RAW_BYTES;
byte raw[TOTAL_RAW_BYTES];
uint16_t parsed[8];
int x = 0;

void setup()
{
  pinMode(BUSY, INPUT);
  pinMode(RESET, OUTPUT);
  pinMode(START_CONVERSION, OUTPUT);
  pinMode(CHIP_SELECT, OUTPUT);

  Serial.begin(115200);
  SPI.begin();
  digitalWrite(START_CONVERSION, HIGH);
  digitalWrite(CHIP_SELECT, HIGH);
  digitalWrite(RESET, HIGH);
  delay(100);
  digitalWrite(RESET, LOW);
  delay(100);
}

void loop()
{
  start = micros();
  int i;
  digitalWrite(START_CONVERSION, LOW);
  delayMicroseconds(100);
  digitalWrite(START_CONVERSION, HIGH);  
  while (digitalRead(BUSY) == HIGH) {
    delayMicroseconds(1);
  }
  SPI.beginTransaction(SPISettings(16000000, MSBFIRST, SPI_MODE0));
  digitalWrite(CHIP_SELECT, LOW);
  while (bytesToRead > 0) {
    raw[TOTAL_RAW_BYTES - bytesToRead] = SPI.transfer(0x00);
    bytesToRead--;
  }
  digitalWrite(CHIP_SELECT, HIGH);
  SPI.endTransaction();

  bytesToRead = TOTAL_RAW_BYTES;

  parseRawBytes();

  for (int i = 0; i < 8; i++) {
    Serial.print((float)parsed[i] * SCALE_FACTOR, 5);
    Serial.print(",");
  }
  Serial.print("\r\n");
  stop = micros();

  Serial.print("time:\t\t");
  Serial.println(stop - start);
}

void parseRawBytes() {
  parsed[0] = (raw[0] << 😎 + raw[1];
  parsed[1] = (raw[2] << 😎 + raw[3];
  parsed[2] = (raw[4] << 😎 + raw[5];
  parsed[3] = (raw[6] << 😎 + raw[7];
  parsed[4] = (raw[8] << 😎 + raw[9];
  parsed[5] = (raw[10] << 😎 + raw[11];
  parsed[6] = (raw[12] << 😎 + raw[13];
  parsed[7] = (raw[14] << 😎 + raw[15];
}

 

とりあえずすんなり動きました。

 

保証する訳でも、利益になる訳でもありませんが、探すのが大変だと思いますのでADボードの購入先を紹介します。

購入先 

 

今後の予定ですが、1chだけを使って割り込みでサンプリング速度が100kHzまで上げられるかどうか確認しようと思います。

8ch同時サンプリングが気に入った方はぜひ発展させてください。各チャンネル200kSPSらしいのでそれはそれで面白そうです。

 

 

メッセージ1/8
3,971件の閲覧回数

データシート見ると、8chすべてにサンプルホールドがあって、ホールドした値を800kS/sでスキャンすることで8ch同時100kSPSを実現してるようですね。4chだと200kSPSなのかな。
すると1チャンネルなら800kSPS ?!

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

いい反応ですね。やはり、8ch全てサンプルホールドしてからAD変換するデバイスは珍しいですか?

私はあまり見たことがありませんでした。

 

データシートには「while sampling at throughput rates up to 200 kSPS for all channels. 」と書かれていて、

AI自動翻訳テック体験Lab『お試し翻訳』で翻訳させると

「サンプリングは、すべてのチャネルで最大200 kSPSのスループットレートで実行されます。」となります。

 

『お試し翻訳』でデータシート23ページの「Track-and-Hold Amplifiers」の項を翻訳すると、以下のようになります。

*****************************

AD 7606/AD 7606-6/AD 7606-4のトラックアンドホールドアンプにより、ADCは16ビット分解能のフルスケール振幅の入力正弦波を正確に取得することができます。トラックアンドホールドアンプは、CONVST xの立ち上がりエッジでそれぞれの入力を同時にサンプリングします。トラックアンドホールド(つまり、外部のCONVSTx信号と実際にホールド状態になるトラック・アンド・ホールドの間の遅延時間)の開口時間は、設計上、1つのデバイス上の8つのトラックアンドホールドすべてとデバイス間でよく一致しています。このマッチングにより、システム内で複数のAD 7606/AD 7606-6/AD 7606-4デバイスを同時にサンプリングできます。
8つのチャネルすべてにわたる変換プロセスの終了は、BUSYのFalling Edgeによって示されます。この時点で、トラックアンドホールドはトラックモードに戻り、次の変換セットの取得時間が開始されます。
パートの変換クロックは内部で生成され、全チャンネルの変換時間はAD 7606で4μs、AD 7606-6で3μs、AD 7606-4で2μsである。AD 7606では、8回の変換がすべて終了するとBUSY信号がlowに戻り、変換処理の終了を示します。BUSYのフォーリングエッジでは、トラックアンドホールドアンプはトラックモードに戻ります。新しいデータは、BUSYがローになった後、パラレル、パラレルバイト、またはシリアルインターフェイスを介して出力レジスタから読み取ることができます。または、BUSYが高いときに前回の変換のデータを読み取ることもできます。変換中にAD 7606/AD 7606-6/AD 7606-4からデータを読み込むと、パフォーマンスにほとんど影響せず、より高速なスループットを実現できます。VDRIVE>3.3 Vの並列モードでは、変換中の読み取り時にSNRが~1.5 dB低下します。

*****************************

8ch分を4μsecで変換していると書いています。1chあたり0.5μsecですが、指定したチャンネルだけ変換することはできないようです。(AD 7606-4は同じシリーズの4chの素子ですが、2μsで変換するので、AD 7606-4を2個使うと8chを2μsで変換することができるようです。)

8ch全て使うとすれば、一般的な多チャンネルAD変換デバイスでは1.6MSPS相当ということでしょうか。

 

 

0 件の賞賛
メッセージ3/8
3,913件の閲覧回数

S&Hを使って複数チャンネル同時サンプリングを実現するのはADCが高価だった昔からある方法で珍しくはありません。

8chの変換に4μsということはADCは2MS/sの実力があるということですね。

トータル100SPS/chなのは、次の値をS&Hするための時間を空けているからかもしれません。

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

>>S&Hを使って複数チャンネル同時サンプリングを実現するのはADCが高価だった昔からある方法で珍しくはありません.

そうなんですね。私が意識していなかっただけのようです。

 

 

デバイスとしては200kSPSを謳っていますが、そのスピードで変換したデータを取り込むのは私には難しいかもしれないので、とりあえずADS8688と同じ1chで100kSPSを目標にしています。1chで200kSPSで取り込めればラインセンサーTCD1304の仕様内(下限)で使えるので嬉しいということになります。

0 件の賞賛
メッセージ5/8
3,879件の閲覧回数

1chだけを読み出すプログラムに変更しました。

SPIの読み込みを8bitから16bitに変更、CS制御をSPIライブラリに任せるように変更なども入れました。

 

/*
    AD7606 ESP32 SPI
    
    D7(MISO)--IO19
    BUSY--IO35
    CS--IO5
    RD(CLK)--IO18
    RST--IO34
    CB,CA--IO33
    RANGE--GND(+/-5V)
    OS0,OS1,OS2--GND(No Over Sampling)
    GND--GND
    5V--5V
*/
//Special Thanks to 
//https://forum.pjrc.com/threads/64097-Teensy-3-2-with-AD7606

#include <SPI.h>
#define BUSY 35
#define RESET 34
#define START_CONVERSION 33

#define SCK 18
#define MISO 19
#define MOSI 23//Not Connected
#define CS 5

uint16_t dataRead16;
#define SCALE_FACTOR 0.000152587890625

void setup(){
  pinMode(BUSY, INPUT);
  pinMode(RESET, OUTPUT);
  pinMode(START_CONVERSION, OUTPUT);
  Serial.begin(115200);
  
  SPI.begin(SCK, MISO, MOSI, CS);
  SPI.setHwCs(true); //Hardware control Cs pin
  digitalWrite(START_CONVERSION, HIGH);
  resetAD7606();
}

void loop(){
  int i;
  digitalWrite(START_CONVERSION, LOW);
  delayMicroseconds(1); //minimum 25ns
  digitalWrite(START_CONVERSION, HIGH);
  
  while (digitalRead(BUSY) == HIGH) {
    delayMicroseconds(1);
  }
  SPI.beginTransaction(SPISettings(16000000, MSBFIRST, SPI_MODE0));
  dataRead16=SPI.transfer16(0x0000);
  SPI.endTransaction();
  Serial.println((float)dataRead16 * SCALE_FACTOR, 5);
}

void resetAD7606(){
  digitalWrite(RESET, HIGH);
  delayMicroseconds(1); //tipically 50ns
  digitalWrite(RESET, LOW);
  delayMicroseconds(1);
}

 

処理が見やすくなったと思います。

 

 

0 件の賞賛
メッセージ6/8
3,871件の閲覧回数

割り込み処理で一定間隔のサンプリングをして、4096個のデータを集めて、TCPサーバーにリクエストが来たときに送り出します。ADS8688の時のプログラムを少し変更すればできます。

受け取り側のLabVIEWプログラムはデータを受け取ってからI16に変換します。AD7606の設定が +/- 5Vで16ビットだからです。

100kHz.png

青がCS信号で、オレンジがクロックです。

F0020TEK.BMPF0021TEK.BMP

 

130kHzまではOKのようですが、140kHzで割り込みに間に合わなくなりました。

このプログラムでは変換開始パルスを出して、Busy信号の解除を待たずに1回前のデータを読み出しています。(Busy解除を待っているとかなり遅くなります。)

 

 

//esp32 board manager ver2.0.6
//Arduino ide 1.8.19
//Mac OS 12.5.1
//Koji Ohashi 230125
/*
    AD7606 ESP32 SPI
    
    D7(MISO)--IO19
    BUSY--IO35
    CS--IO5
    RD(CLK)--IO18
    RST--IO34
    CB,CA--IO33
    RANGE--GND(+/-5V)
    OS0,OS1,OS2--GND(No Over Sampling)
    GND--GND
    5V--5V
*/
//Special Thanks to 
//https://forum.pjrc.com/threads/64097-Teensy-3-2-with-AD7606


#include <SPI.h>
#define BUSY 35
#define RESET 34
#define START_CONVERSION 33

#define SCK 18
#define MISO 19
#define MOSI 23//Not Connected
#define CS 5

//uint16_t dataRead16;
#define SCALE_FACTOR 0.000152587890625

//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];



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 50000.0// 50kHz
//#define LEDC_BASE_FREQ_TrigAI 80000.0// 80kHz
#define LEDC_BASE_FREQ_TrigAI 100000.0// 100kHz
//#define LEDC_BASE_FREQ_TrigAI 130000.0// 130kHz--OK
//#define LEDC_BASE_FREQ_TrigAI 140000.0// 140kHz--NG
//#define LEDC_BASE_FREQ_TrigAI 200000.0// 200kHz

#define DUTY_TrigAI 8//50% 0x20 = 32
#define GPIO_PIN_TrigAI 26 //GPIO #36~#39 は設定不可

//割り込み関数***************
void IRAM_ATTR readAI(){
  digitalWrite(START_CONVERSION, LOW);
  delayMicroseconds(1); //minimum 25ns
  digitalWrite(START_CONVERSION, HIGH);
  digitalWrite(START_CONVERSION, HIGH);//dummy for wait
  dataArray[read_count]=SPI.transfer16(0x0000);
  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);

  pinMode(BUSY, INPUT);
  pinMode(RESET, OUTPUT);
  pinMode(START_CONVERSION, OUTPUT);
  Serial.begin(115200);
  
  SPI.begin(SCK, MISO, MOSI, CS);
  SPI.setHwCs(true); //Hardware control Cs pin
  digitalWrite(START_CONVERSION, HIGH);
  resetAD7606();

}

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_MODE0));
  
  attachInterrupt(GPIO_PIN_TrigAI, readAI, RISING);//SOの読み取りタイミング
  while(read_count<DATA_SIZE){}  //全画素取り込み
  detachInterrupt(GPIO_PIN_TrigAI);//SOの読み取り終了
  
  SPI.endTransaction();
}



void resetAD7606(){
  digitalWrite(RESET, HIGH);
  delayMicroseconds(1); //tipically 50ns
  digitalWrite(RESET, LOW);
  delayMicroseconds(1);
}


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;
  }
}

 

 

ESP32用のプログラムとLabVIEWプログラムを添付します。

ノコギリ波のデータを見ていると時々ノコギリのスロープが部分的に膨らむようなグラフが表示されます。原因はわかりませんが、ADS8688の方が安心かなという気になります。

不安定.png

 

 

0 件の賞賛
メッセージ7/8
3,856件の閲覧回数

うまく添付できていなかったので、プログラムを添付します。

0 件の賞賛
メッセージ8/8
3,852件の閲覧回数