北東北 LabVIEWユーザー会

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

ADS8688を活用したラインCCD (ILX511)の分解能向上とイベント駆動キュードステートマシン

祝<<NI as エマソンのテスト計測事業グループ>>このフォーラムも今のところ変わらないですね。

 

PB191218.JPG

 

久しぶりにLabVIEWでプログラムを書きました。

これまではできるだけ速く沢山データを取れるように生産者・消費者パターンを使っていましたが、PC側で分光データのデータ処理も柔軟に対応できるようにイベント駆動キュードステートマシンを使うことにしました。The LabVIEW Style Bookをペラペラ眺めて思い出したのですが、「Event-Driven State Machine」という名前で説明されていて、キュードステートマシンのアイドルステートにイベントストラクチャーを組み込んだものです。

 

スクリーンショット 2023-11-19 8.53.38.png

 

スクリーンショット 2023-11-19 9.05.51.png

 

ADS8688については「SPI接続のAD変換ボード(ADS8688 8ch 500kSPS)を使ってみる」で書いていますし、ラインCCD (ILX511)については「2048ピクセルのCCDラインセンサILX511(SONY)をESP32で制御してLabVIEWで分光データを受け取る」で開いていますので、それぞれ問題ないのですがESP32のピンアサインが重複しているのを変更する必要があります。ESP32のSPIはデフォルトでピン番号が決まっていますので、それを尊重して割り当てました。

 

ESP32のピンアサイン

//for ADS8688 SPIピン
//SCK---IO18
//MISO---IO19
//MOSI---IO23
//CS0---IO5

//for SONY ILX511
//CLK IO16
//ROG IO17
//Trig_RO IO21 //internal use

 

//Koji Ohashi MaDA Lab@Morioka 231114
//ILX511_ADS8688_Esp32_231119
//Software:  Arduino IDE 1.8.19
//Software:  Arduino core for the ESP32 2.0.6

//パルス生成:Special thanks to 
//https://www.mgo-tec.com/blog-entry-ledc-pwm-arduino-esp32.html/2
//https://logikara.blog/pwm/
//割り込み処理:Special thanks to
//https://lang-ship.com/blog/work/esp32-freertos-l04-interrupt/


#include <SPI.h>

//for ADS8688 SPIピン
//SCK---IO18
//MISO---IO19
//MOSI---IO23
//CS0---IO5
//VOUT Channel_0 Trig_ROがFallのタイミングで読む

//for SONY ILX511----ADS8688導入後
//バッファ用反転ICが入るのでプログラムのIOは反転
//CLK IO5--->IO16
//ROG IO18--->IO17
//Trig_RO IO26--->IO21 //internal use


#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 5


//ROG
#define ROG_PIN 17
#define ROG_WIDTH 100 //100μsec
int ROG_IntervalMs=500;

//CLK
#define CLK_PIN 16 //GPIO #36~#39 は設定不可
#define LEDC_CHANNEL_CLK 0 //CHANNEL_0 (CHANNEL_0とCHANNEL_1は周波数とタイマービットを同一値)
#define LEDC_TIMER_BIT_CLK 6 //6bit--64(0-63)
#define LEDC_BASE_FREQ_CLK 10000.0 //AnalogReadに時間がかかって13kHzが上限
#define DUTY_CLK 32//50% 0x20 = 32

//TrigRO
#define GPIO_PIN_TrigRO 21 //GPIO #36~#39 は設定不可
#define LEDC_CHANNEL_TrigRO 1 //CHANNEL_1 (CHANNEL_0とCHANNEL_1は周波数とタイマービットを同一値)
#define LEDC_TIMER_BIT_TrigRO 6 //6bit--64(0-63)
#define LEDC_BASE_FREQ_TrigRO 10000.0 //AnalogReadに時間がかかって13kHzが上限
#define DUTY_TrigRO 32//50% 0x20 = 32

#define PIXELS 2086
volatile uint16_t pixelArray[PIXELS];
volatile int read_count=0;
volatile uint16_t sensorValue=0;
volatile boolean risingEdge=false;
byte FOOTER[]={0x0F,0x0F,0x0F,0x0F};
byte SendBytes[2];
boolean SendFlag = false;

//割り込み関数***************
void ARDUINO_ISR_ATTR readRO(){  
  sensorValue = cmdRegister(NO_OP);//get data
  pixelArray[read_count]=sensorValue;
  read_count++;
}
void ARDUINO_ISR_ATTR risingCLK(){
  risingEdge=true;
}
//割り込み関数END************


void writeArrayData(){
  for(int i=0;i<PIXELS;i++){
    sensorValue = pixelArray[i];
    SendBytes[0] = byte(sensorValue);
    SendBytes[1] = byte(sensorValue >> 8);
    Serial.write(SendBytes,2);
  }
  Serial.write(FOOTER,4);
}


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

void otherCommand(String cmd){
  String commandType = cmd.substring(0,3);
  if(commandType=="exp"){
    String exposureMs = cmd.substring(3);
    ROG_IntervalMs = exposureMs.toInt();
  }
}


void setup() {
  Serial.begin(230400);
  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
  
  pinMode(ROG_PIN,OUTPUT);
  pinMode(CLK_PIN,OUTPUT);
  //CLK 必要な時だけ出力する
  ledcSetup(LEDC_CHANNEL_CLK, LEDC_BASE_FREQ_CLK, LEDC_TIMER_BIT_CLK);

  //TrigRO 割り込みに使用するため常に出力する
  ledcSetup(LEDC_CHANNEL_TrigRO, LEDC_BASE_FREQ_TrigRO, LEDC_TIMER_BIT_TrigRO);
  ledcAttachPin(GPIO_PIN_TrigRO, LEDC_CHANNEL_TrigRO);
  ledcWrite(LEDC_CHANNEL_TrigRO, DUTY_TrigRO);
}
 
void loop() {
  unsigned long startCLK = millis();
  
  digitalWrite(CLK_PIN,LOW);
  delayMicroseconds(50); //10kHzの周期の1/2
  digitalWrite(CLK_PIN,HIGH);
  delayMicroseconds(50); //10kHzの周期の1/2
  
  digitalWrite(ROG_PIN,HIGH);
  digitalWrite(CLK_PIN,LOW);
  delayMicroseconds(ROG_WIDTH);//100μSec
  digitalWrite(ROG_PIN,LOW);
  // CLKに使うパルスがHIGHになるまで待つ
  risingEdge=false;
  attachInterrupt(GPIO_PIN_TrigRO, risingCLK, RISING); //割り込み開始
  while(risingEdge==false){ //wait rising edge
  }
  digitalWrite(CLK_PIN,HIGH); //CLKをHIGHにする
  detachInterrupt(GPIO_PIN_TrigRO);//割り込み解除
  ledcAttachPin(CLK_PIN, LEDC_CHANNEL_CLK);//CLKパルスを出力
  ledcWrite(LEDC_CHANNEL_CLK, DUTY_CLK);//CLKパルスを出力
  
  read_count=0;
  attachInterrupt(GPIO_PIN_TrigRO, readRO, FALLING);
  //Reading CCD data
  while(read_count < PIXELS){
    //全てのPIXELを読み込むまで待つ
  }
  detachInterrupt(GPIO_PIN_TrigRO);

  ledcDetachPin(CLK_PIN); //CLKパルスを停止
  digitalWrite(CLK_PIN,HIGH); //CLKをHIGHにする

  if(SendFlag==true){
    writeArrayData();//Send Pixel Data
  }
  SendFlag=false;

  while((millis()-startCLK) < ROG_IntervalMs){
    //wait ROG Interval
    if(Serial.available()){      //check command from Serial port
      String command = Serial.readStringUntil(':');
      if(command=="send"){
        SendFlag=true;//exposure
      } else {
        otherCommand(command);
      }
    }
  }
}

 

ラインCCDからのデータの読み出しは常に繰り返している中で、LabVIEW側から”send:”というコマンドがくるとデータを書き出します。露光時間も"exp500:"というコマンドで500msec、"exp2000:"で2000msecになるようにしました。

 

スクリーンショット 2023-11-19 9.06.29.png

データの読み取りはデータの末尾に

byte FOOTER[]={0x0F,0x0F,0x0F,0x0F};

を付けて目印にしました。

スクリーンショット 2023-11-19 9.06.46.png

ESP32用のArduinoプログラムとLabVIEW2023用のLabVIEWプログラムを添付します。LabVIEWプログラムの作成はMacで行なって、WindowsPCで動作することを確認しました。

 

 

 

 

 

 

 

 

 

 

 

 

0 件の賞賛
メッセージ1/1
577件の閲覧回数