// Logana_revA3 Processing3 2021-10-30 import controlP5.*; import java.util.*; import processing.serial.*; ControlP5 cp5; ScrollableList[] SL = new ScrollableList[5]; RadioButton[] RB = new RadioButton[3]; Toggle[] TG = new Toggle[3]; Toggle bt; Serial myPort = null; // シリアルポート List<String> portList; CColor bc = new CColor(); CColor fc = new CColor(); String RxStr = ""; // 受信中の文字列 String waveStr = ""; // 描いている文字列 boolean indON = false; // トリガインディケータ boolean isDataRxing = false; // データ文字列受信中 boolean rptON = false; // 連続モード boolean nonTrg = false; // 「トリガなし」出力 boolean cursors = false; // 「cursors」出力 boolean dragging = false; // ドラグ中か int pntTrg = 100; // トリガ位 int runMode = 0; // 単発 連続 float zoomX = 1.0; // スクリーン倍率 int sampRate = 5; // サンプルレート int repeetTimeUp; // 連続実施までの時間 // 定数の定義 ---------------------------- final int sSpeed = 115200; // シリアル通信速度 // データ描画用スクリーン位置定数 //final int srnLeft = 40; final int srnLeft = 40; final int srnTop = 200; //final int srnWidth = 550; final int srnWidth = 512; final int srnHight = 160; final int srnRight = srnLeft + srnWidth; final int srnBottom = srnTop + srnHight; // コントロール枠位置定数 final int crlTop = 50; final int ctlTrgLeft = 10; final int ctlAquLeft = ctlTrgLeft+200; final int ctlRunLeft = ctlAquLeft+200; // スクロールバー定数 final int BarL = srnLeft; final int BarT = srnBottom+2; final int BarW = srnWidth; final int BarH = 20; final int BarR = BarL+BarW; final int BarB = BarT+BarH; final int wBarT = BarT+1; final int wBarH = BarH-2; // アクティブウインドバー定数 int wBarL = srnLeft; int wBarW = srnWidth/2; int wBarR = wBarL+wBarW; int fBar = 2; // Barの縮尺 // データポインタ int dStart = 0; // 描き始める受信データ位置 int dEnd = srnWidth; // 描き終わる受信データ位置 int dStep = 1; // 描く受信データ間隔 int sStep = 1; // 描くスクリーン間隔 //カーソルライン位置(データ座標) int posCurDL = pntTrg; int posCurDR = pntTrg; final color cGray = color(128); final color cBlk = color(0); final color cWit = color(255); final color cRed = color(255,0,0); final color cGrn = color(0,255,0); final color cRed_r = color(0,255,255); final color cGrn_r = color(255,9,255); color cStr = cWit; color cActBtn = color(50,150,240); color cBgGroup = cBlk; color cDataLine = cGrn; color cActRun = color(150,100,0); color cBakRun = color(0,100,0); color cForRun = color(0,127,0); color cTrig = cRed; color cIndOn = cRed; color cIndOff = cBlk; color cBgBtn = color(80); color cFgBtn = cGray; color cBkGnd = color(50); color cCursX = color(110,170,255); color cCursY = color(255,140,255); final List<String> trgList = Arrays.asList( "*", "1", "0", "/", "\"); final List<String> sampList = Arrays.asList( " 1 us ( 1MHz)"," 2 us (500kHz)", " 5 us (200kHz)"," 10 us (100kHz)", " 20 us ( 50kHz)"," 50 us ( 20kHz)", "100 us ( 10kHz)","200 us ( 5kHz)", "500 us ( 2kHz)"," 1 ms ( 1kHz)"); final String[][] strDiv = { {"100us","200us","500us", "1ms", "2ms", "5ms", "10ms", "20ms", "50ms","100ms"}, { "50us","100us","250us", "500us", "1ms", "2.5ms", "5ms", "10ms", "25ms", "50ms"}, { "25us", "50us","125us", "250us","500us", "1.25ms","2.5ms", "5ms","12.5ms", "25ms"}}; final int[] usPerDot = { 1,2,5,10,20,50,100,200,500,1000}; void setup() { size(570, 430); cp5 = new ControlP5(this); cp5.setColorBackground(cBgBtn); // 明るく見やすく PFont myFont = createFont("Verdana",12); ControlFont cf1 = new ControlFont(myFont); textFont(myFont); cp5.setFont(cf1); portList = Arrays.asList(Serial.list()); int portIndex = portList.size()-1; // 各CHのトリガ条件の設定:( name, x, y, width, height ) for(int i=3;i>0;i--){ SL[i] = cp5.addScrollableList( "CH"+i,ctlTrgLeft+40,crlTop+(i*25),50,120) .setBarHeight(20) .setItemHeight(20) .addItems(trgList).setValue(0); } SL[1].setValue(3); // ポート選択の設定 ( name, x, y, width, height ) SL[0] = cp5.addScrollableList( "portSelect",20,20,220,160) .setBarHeight(20).setItemHeight(20) .addItems(portList).setValue(portIndex); // トリガと収集データの相対位置の設定ボタン RB[0] = cp5.addRadioButton( "r1SampPos",ctlAquLeft+50,crlTop+50) .setSize(15,15).setColorLabel(cStr) .setItemsPerRow(1).setSpacingColumn(100) .addItem("トリガの後方",0) .addItem("トリガの前後",4) .addItem("トリガの前方",5) .activate(0); // サンプルレートの設定 SL[4] = cp5.addScrollableList( "srSampRate",ctlAquLeft+25,crlTop+25,150,220) .setBarHeight(20).setItemHeight(20) .addItems(sampList).setValue(5); // RUN/STOPボタンの設定:( name,初期値 ) bt = cp5.addToggle("btnRun",false) .setPosition(ctlRunLeft+20,crlTop+15).setSize(90,30) .setColorActive(cActRun) // ON色 .setColorBackground(cBakRun) // OFF色 .setColorForeground(cForRun); // マウス選択色 bt.setCaptionLabel("RUN").getCaptionLabel() .align(ControlP5.CENTER, ControlP5.CENTER); // 単発/連続のラジオボタン RB[1] = cp5.addRadioButton( "r2RunMode",ctlRunLeft+20,crlTop+60) .setSize(15,15).setColorLabel(cStr) .setItemsPerRow(2).setSpacingColumn(40) .addItem("単発",0).addItem("連続",1) .activate(0); // トリガなしのボタン TG[0] = cp5.addToggle("nonTrg",false) .setPosition(ctlRunLeft+20,crlTop+85).setSize(15,15) .setCaptionLabel("トリガなし"); // カーソルOnOffボタン TG[1] = cp5.addToggle("cursors",false) .setPosition(135,srnTop-20).setSize(15,15) .setCaptionLabel("Cursors"); // 背景色変更ボタン TG[2] = cp5.addToggle("whiteBack",false) .setPosition(ctlRunLeft+80,20).setSize(15,15) .setCaptionLabel("色 反転"); for(Toggle t:TG){ t.setColorLabel(cStr).getCaptionLabel() .align(ControlP5.RIGHT_OUTSIDE, ControlP5.CENTER) .toUpperCase(false) .setPaddingX(5) ; } // スケールのラジオボタン RB[2] = cp5.addRadioButton("rbZoom",50,400) .setSize(15,15).setColorLabel(cStr) .setColorLabel(cStr) .setItemsPerRow(3).setSpacingColumn(40) .addItem(".5x",0) .addItem("1x",1) .addItem("2x",2) .activate(1); } void draw() { // 連続モードの処理を行う if(rptON){ // 繰り返し時間になったら if(repeetTimeUp < millis()){ btnRun(true); // RUNボタンを押す rptON = false; // データ受信を待つ } } background(cBkGnd); drawScreen(); // 背景画面 描画 drawData(); // データ線 描画 indControl(); // 受信ライト 処理 drawCursor(); // カーソル 描画 } // ================================ // 画面背景を描く width height // ================================ void drawScreen(){ // ウインド枠を灰色にする stroke(cGray); fill(cBkGnd); rect(1,1,width-2, height-2); // データスクリーン --------------------- stroke(cGray); // 枠を灰色に fill(cBgGroup); rect(srnLeft+1,srnTop+1,srnWidth,srnHight); // ch番号及び data GND level------------- stroke(cGray); fill(cStr); textSize(12); int j = 1; for(int vPos=srnTop+30;vPos<srnBottom;vPos+=30){ text("Ch"+j++,srnLeft-32,vPos-5); line(srnLeft,vPos,srnRight,vPos); } // 小目盛は、10dot毎 stroke(cGray); // int scaleStart = srnLeft+10-(dStart*sStep%10); for(int i=scaleStart;i<srnRight;i+=10){ line(i,srnTop,i,srnTop+5); line(i,srnBottom,i,srnBottom-5); } // 大目盛は、50dot毎 scaleStart = srnLeft+50-(dStart*sStep%50); for(int i=scaleStart;i<srnRight;i+=50){ line(i,srnTop,i,srnBottom); } // スクロール Bar ------------------- stroke(cGray); // 枠の色 fill(cBgGroup); // 外ウインドの色 rect(BarL,BarT,BarW,BarH); // 外ウインド描く fill(cBkGnd); // 内ウインドの色 rect(wBarL,wBarT,wBarW,wBarH); // 内ウインド描く // コントロール枠 ------------------------- stroke(128); // 枠を灰色に fill(cBgGroup); //rect(10,crlTop,260,110,10); rect(ctlTrgLeft,crlTop,190,110,10); rect(ctlAquLeft,crlTop,190,110,10); rect(ctlRunLeft,crlTop,140,110,10); // コントロール枠内ラベル --------------------- fill(cStr); textSize(12); text("<トリガ条件>",ctlTrgLeft+5,crlTop+18); text("Ch1",ctlTrgLeft+8,crlTop+40); text("Ch2",ctlTrgLeft+8,crlTop+65); text("Ch3",ctlTrgLeft+8,crlTop+90); text("*:なし", ctlTrgLeft+120,crlTop+30); text("1:Hi", ctlTrgLeft+120,crlTop+46); text("0:Low", ctlTrgLeft+120,crlTop+62); text("/:立上り",ctlTrgLeft+120,crlTop+78); text("\:立下り",ctlTrgLeft+120,crlTop+94); text("<サンプル条件>",ctlAquLeft+5,crlTop+18); // トリガ線を引く ------------------- vLine(cTrig, datX_appX(pntTrg)); // Div時間の表示 ------------------- String str; str = strDiv[int(zoomX)][sampRate] + " / Div"; fill(cStr); textSize(12); text(str,srnLeft,srnTop-7); fill(cGray); text("211030 IWA",470,420); } // スクリーンに縦線を引く void vLine(color c, int p){ if(p == 0) return; stroke(c); line(p,srnTop,p,srnBottom); } // +++++++++++++++++++++++++++++++++ // 座標変換 データ座標 ー> アプリ座標 // 受信データの位置を作画のための座標に // +++++++++++++++++++++++++++++++++ int datX_appX(int dX){ int num = srnLeft + int((float)(dX - dStart) * zoomX); if(num <= srnLeft) return 0; if(num >= srnRight) return 0; return num; } // +++++++++++++++++++++++++++++++++ // 座標変換 アプリ座標 ー> データ座標 // 作画のための座標を受信データの位置に // +++++++++++++++++++++++++++++++++ int appX_datX(int aX){ return int(float(aX - srnLeft)/zoomX)+dStart; } // データ線 描画 +++++++++++++++++++++++++++++ // waveStrを一文字ずつデータ線に書いていく //++++++++++++++++++++++++++++++++++++++++++ void drawData(){ int xPos; // 描いている現データのX座標 int dataNow; // 処理している 現データ int dataNext; // 次に処理する 次データ int diffData; // 現データと次データの変位 int ch; // 描いているチャネル int testBit; // 上記のチャネルbit位置 int HiPos; // 上記のチャネルHiレベル描画位置 int LoPos; // 上記のチャネルLowレベル描画位置 // 各データの数だけ繰り返す ---------------------------- int endPos = (waveStr.length())-2; if(endPos>dEnd)endPos = dEnd; // xPos = srnLeft+int(zoomX); for (int i=dStart;i<endPos;i+=dStep){ // データ毎の初期設定 // testBitをCh1を示すBit位置に戻す // 文字列から必要データを抜き出し、bit値に直す // 次のデータとのLogic変位を計算 testBit = 1; dataNow = int(waveStr.charAt(i)) & 0x1F; dataNext = int(waveStr.charAt(i+dStep)) & 0x1F; diffData = dataNow ^ dataNext; // 各チャネルの描画 5Ch分繰り返す --------------------- stroke(cDataLine); for (ch = 0; ch < 5; ch++){ // Logic Hi Low の画描位置を計算 HiPos = ch*30+10+srnTop; LoPos = HiPos + 20; // 現在のレベル位置に横線を引く if ((dataNow & testBit) == 0) line(xPos,LoPos,xPos+1,LoPos); else line(xPos,HiPos,xPos+1,HiPos); // 次のデータのレベルが変位していれば、縦線を引く if ((diffData & testBit) != 0) line(xPos + sStep,HiPos,xPos + sStep,LoPos); testBit <<= 1; // 次のチャンネル準備 } // 次のチャンネルへ xPos += sStep; // 次のデータ描画位置 } // 次のデータへ } // 受信ライト 処理 ++++++++++++++++++++++++++++ // indON の値に従い受信ライト On/Off //++++++++++++++++++++++++++++++++++++++++++++ void indControl() { stroke(80); if(indON) fill(cIndOn); else fill(cIndOff); circle(ctlRunLeft+120,crlTop+30, 10); } // ========================================= // カーソルを描く // ========================================= void drawCursor(){ if(!cursors) return; // カーソル Off なら復帰 String str; int nX,nY; // カーソル線の表示 vLine(cCursX, datX_appX(posCurDL)); vLine(cCursY, datX_appX(posCurDR)); // カーソル数値を表示 fill(cCursY); nY = timeToTrg(posCurDR); str = "Y = " + usms(nY); text(str,srnLeft+280,srnTop-7); fill(cCursX); nX = timeToTrg(posCurDL); str = "X = " + usms(nX); // X-Y の間隔を表示 text(str,srnLeft+180,srnTop-7); str = "|X-Y| = " + usms(abs(nX-nY)); str += hzkhz(abs(nX-nY)); text(str,srnLeft+380,srnTop-7); } // カーソル位置からトリガまでの時間usを計算 int timeToTrg(int num){ return (num - pntTrg) * usPerDot[sampRate]; } // 数値を us/msで表示 String usms(int num){ String str = ""; int aN = abs(num); if(aN >= 100000){ str = nf(float(num)/1000,0,0)+" ms"; }else if(aN >= 10000){ str = nf(float(num)/1000,0,1)+" ms"; }else if(aN >= 1000){ str = nf(float(num)/1000,0,2)+" ms"; }else{ str = nf(num,0)+" us"; } return str; } // 数値を hz/khzで表示 String hzkhz(int num){ if(num==0)return " (-- Hz)"; String str = " ("; float fnum = 1000000/float(num); if(fnum >= 1000){ str += nf(fnum/1000,0,2)+" kHz)"; }else if(fnum >= 100){ str += nf(int(fnum),0)+" Hz)"; }else{ str += nf(fnum,0,1)+" Hz)"; } return str; } // ================================ // シリアルポートが選択された時 // ================================ void portSelect(int n) { // 既に接続されていれば、切断して // 新しい選択項目は、index n で接続する if ( myPort != null ) myPort.stop(); myPort = new Serial(this, portList.get(n), sSpeed); myPort.bufferUntil('\n'); } // ================================ // シリアル受信した時 // ================================ void serialEvent(Serial port) { // 受信データがあれば、内容を確認して処理 if ( port.available() > 0 ) { String str = port.readString(); str = trim(str); if (str != null ){ // PIC側送信完了なら --------------- if (str.equals("##")){ waveStr = RxStr; // 受信列を波形列とする indON = false; // 受信ライト OFF isDataRxing = false; // 受信中を 否 if(runMode==0){ // 単発なら rptON = false; // rptONを 否 bt.setValue(0); // ボタン緑へ }else{ // 連続なら // 連続なら、500ms後に再びRUN操作をする // ために、repeetTimeUp を設定 repeetTimeUp = millis()+500; rptON = true; // rptONを 真 } // PIC側コマンド正常受信なら --------------- }else if (str.equals("#")){ RxStr = ""; // 受信列をクリア isDataRxing = true; // 受信中を 真 // PIC側コマンドエラー受信なら --------------- }else if (str.equals("!")){ RxStr = ""; // PIC側STOP受信なら --------------- }else if (str.equals("!!")){ RxStr = ""; // PIC側のデータを受信した --------------- }else if (isDataRxing){ indON = true; // 受信ライト OFF RxStr += str; // 受信列に追加 } } } } // +++++++++++++++++++++++++++++++++ // RUN/STOPボタンを押した時 // +++++++++++++++++++++++++++++++++ void btnRun(boolean theFlag) { String cmdStr = ""; // RUNボタンを押した時 if(theFlag) { //waveStr = ""; String cmdSamp,cmdTrg; bt.setCaptionLabel("STOP"); cmdSamp = str((int)SL[4].getValue()); cmdTrg = str((int)RB[0].getValue()); cmdStr = "#" + str((int)SL[1].getValue()) + str((int)SL[2].getValue()) + str((int)SL[3].getValue()); if(cmdStr.equals("#000"))cmdTrg="3"; if(nonTrg)cmdTrg = "3"; switch(cmdTrg){ case "0":pntTrg = 100;break; case "3":pntTrg = 0;break; case "4":pntTrg = 550;break; case "5":pntTrg = 1000;break; } cmdStr += "#"+cmdSamp+cmdTrg+"\n" ; } // STOPボタンを押した時 else { rptON = false; bt.setCaptionLabel("RUN"); if (isDataRxing){ cmdStr = "!"; } } if(cmdStr != ""){ //if(false){ try{ myPort.write(cmdStr); } catch(Exception e) { //msg("シリアル通信エラー"); myPort.stop(); } } } // サンプルレート変更 +++++++++++++++++++ // サンプルレートを変更した時、 // リスト項目の背景を少し明るく見やすく // +++++++++++++++++++++++++++++++++++ void srSampRate(int n) { waveStr = ""; // posCurDL = pntTrg; // カーソルのリセット posCurDR = pntTrg; bc.setBackground(cBgBtn); fc.setBackground(cFgBtn); // 古い選択項目の背景を戻し、新項目の背景を明るく cp5.get(ScrollableList.class, "srSampRate") .getItem(sampRate).put("color", bc); sampRate = n; cp5.get(ScrollableList.class, "srSampRate") .getItem(sampRate).put("color", fc); } // +++++++++++++++++++++++++++++++++ // サンプル位置を変更した時 // 非選択にならないよう処置 // +++++++++++++++++++++++++++++++++ void r1SampPos(int num) { if(num < 0){ // 非選択(-1)なら RB[0].activate(0); // Item 0 を選択 RB[0].setValue(0); } } // +++++++++++++++++++++++++++++++++ // 単発/連続を変更した時 // +++++++++++++++++++++++++++++++++ void r2RunMode(int num) { if(num < 0){ RB[1].activate(0); RB[1].setValue(0); runMode = 0; }else{ runMode = num; } } // +++++++++++++++++++++++++++++++++ // 倍率を変更した時 // +++++++++++++++++++++++++++++++++ void rbZoom(int num) { if(num < 0){ RB[2].activate(1); RB[2].setValue(1); num = 1; } wBarL = 40; switch(num){ case 0: // x0.5 wBarW = srnWidth;fBar = 1; dStep = 2; sStep = 1; zoomX = 0.5; break; case 1: // x1 wBarW = srnWidth/2;fBar = 2; dStep = 1; sStep = 1; zoomX = 1.0; break; case 2: // x2 wBarW = srnWidth/4;fBar = 2; dStep = 1; sStep = 2; zoomX = 2.0; break; } dStart = (wBarL-BarL)*fBar; dEnd = (wBarW- 1) * 2 + dStart; } // +++++++++++++++++++++++++++++++++ // 背景色を変更した時に実行 // +++++++++++++++++++++++++++++++++ void whiteBack(boolean theFlag) { if(theFlag==true) { // 白 背景を選択 cStr = cBlk; cActBtn = color(205,105,15); cBgGroup = cWit; cDataLine = cGrn_r; cActRun = color(105,155,255); cBakRun = color(255,155,255); cForRun = color(255,128,255); cTrig = cRed_r; cIndOn = cRed_r; cIndOff = cWit; cBgBtn = color(175); cBkGnd = color(205); cCursX = color(145,85,0); cCursY = color(0,115,0); } else { // 黒 背景を選択 cStr = cWit; cActBtn = color(50,150,240); cBgGroup = cBlk; cDataLine = cGrn; cActRun = color(150,100,0); cBakRun = color(0,100,0); cForRun = color(0,127,0); cTrig = cRed; cIndOn = cRed; cIndOff = cBlk; cBgBtn = color(80); cFgBtn = cGray; cBkGnd = color(50); cCursX = color(110,170,255); cCursY = color(255,140,255); } cp5.setColorActive(cActBtn) // ON色 .setColorBackground(cBgBtn) // OFF色 .setColorForeground(cActBtn); // OFF色 for(ScrollableList s:SL){ s.setColorLabel(cStr) .setColorValue(cStr); } for(RadioButton r:RB)r.setColorLabel(cStr); for(Toggle t:TG) t.setColorLabel(cStr); bt.setColorActive(cActRun) // ON色 .setColorBackground(cBakRun) // OFF色 .setColorForeground(cForRun) // マウス選択色 .setColorLabel(cStr); bc.setBackground(cBgBtn); fc.setBackground(cFgBtn); for(int i=0;i<10;i++){ SL[4].getItem(i).put("color", bc); } SL[4].getItem(sampRate).put("color", fc); } // マウスプレス ++++++++++++++++++++++++ // カーソルの位置を決める // +++++++++++++++++++++++++++++++++++ void mousePressed(MouseEvent e){ // データスクリーン上か if(cursors && isInSrn()){ if( e.getButton() == LEFT ){ // println( "左が押された" ); posCurDL = appX_datX(pmouseX); } else if( e.getButton() == RIGHT ){ // println( "右が押された" ); posCurDR = appX_datX(pmouseX); } } } // マウスドラッグ +++++++++++++++++++++++ // Barをドラッグしたら、その移動量だけ // Barを移動させる //++++++++++++++++++++++++++++++++++++ void mouseDragged(MouseEvent e){ //スクロールバーをドラッグした場合 if(mouseX == pmouseX)return; if((ptrInBar()==1)| dragging){ dragging = true; wBarL += mouseX - pmouseX; if(wBarL<BarL)wBarL=BarL; if(wBarL>BarR-wBarW)wBarL=BarR-wBarW; // 波形データの描く範囲を計算 dStart = (wBarL-BarL)*fBar; dEnd = (wBarW- 1) * 2 + dStart; } } // マウスリリース ++++++++++++++++++++++ // Barをドラッグ中に、Barエリアから外れても // 処理を継続できるように追加 // +++++++++++++++++++++++++++++++++++ void mouseReleased(MouseEvent e){ dragging = false; } // マウスポインター位置確認 ++++++=++++++= // Mouse Pointer が // ScrollBarの外側なら :0 // ActiveWindowBarの内側なら :1 // ScrollBarの左背景なら :2 // ScrollBarの右背景なら :3 // +++++++++++++++++++++++++++=++++++= int ptrInBar(){ if(pmouseX < BarL)return 0; if(pmouseX > BarR)return 0; if(pmouseY < BarT)return 0; if(pmouseY > BarB)return 0; if(pmouseX < wBarL)return 2; if(pmouseX > (wBarL+wBarW)) return 3; return 1; } // マウスポインター位置確認 ++++++=++++++= // Mouse Pointer が // データスクリーンの外側なら :false // 内側なら :true // +++++++++++++++++++++++++++=++++++= boolean isInSrn(){ if(pmouseX < srnLeft) return false; if(pmouseX > srnRight)return false; if(pmouseY < srnTop) return false; if(pmouseY > srnBottom)return false; return true; }