11-18-2023 10:03 PM
祝<<NI as エマソンのテスト計測事業グループ>>このフォーラムも今のところ変わらないですね。
久しぶりにLabVIEWでプログラムを書きました。
これまではできるだけ速く沢山データを取れるように生産者・消費者パターンを使っていましたが、PC側で分光データのデータ処理も柔軟に対応できるようにイベント駆動キュードステートマシンを使うことにしました。The LabVIEW Style Bookをペラペラ眺めて思い出したのですが、「Event-Driven State Machine」という名前で説明されていて、キュードステートマシンのアイドルステートにイベントストラクチャーを組み込んだものです。
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になるようにしました。
データの読み取りはデータの末尾に
byte FOOTER[]={0x0F,0x0F,0x0F,0x0F};
を付けて目印にしました。
ESP32用のArduinoプログラムとLabVIEW2023用のLabVIEWプログラムを添付します。LabVIEWプログラムの作成はMacで行なって、WindowsPCで動作することを確認しました。