C/C++によるリモートアプリ開発
このページではWindows上で動作するCosmo-Z操作プログラムの開発方法について説明します。
Cosmo-ZではサーバプログラムがTCP/IPで受け取ったコマンドをCosmo-Z APIに渡す仕組みがあります。また、Windows PCで動作するCosmo-Z APIのDLLが用意されていて、Windows上のDLLを操作するとTCP/IPを通じてメモリやレジスタにリード/ライトを要求するコマンドが発行されます。
このようにして、Windows PCなどリモートコンピュータからZYNQのハードウェアにアクセスできるようにしています。
このページでは単純なプログラムを作成して、波形のキャプチャができるようにします。トリガの設定やイベントキャプチャについては後のページで解説します。
最初のプログラム
Cosmo-Zのリモートアプリを開発するには、以下のDLLをダウンロードします。
- Cosmo-Zのリモート操作DLL 2023/8/13
解凍するとlibcsz.dll、libcsz.lib、cosmoz.hの3つのファイルが取り出されますので任意のディレクトリに置いてください。
次に、以下のプログラムを作成し、test.cppとして保存します。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "cosmoz.h" int main (int argc, char *argv[]) { if(argc < 2) { printf("test.exe <hostname>.\n"); return 0; } if(csz_open(INTERFACE_TCPIP, argv[1]) == FALSE) { printf("Cosmoz driver open failed.\n"); return 0; } printf("FPGA Version=%08X\n",csz_fpga_version()); printf("Currnet temperature=%f\n",csz_xadc_tempe()); csz_close(); }
関数の説明
Cosmo-ZのAPI関数はcsz_という名前で始まります。
- csz_open・・・Cosmo-Zに接続するための関数です。リモートアプリケーションを作る場合はINTERFACE_TCPIPを指定します。第二引数にはホスト名またはIPアドレスを入れます。
- csz_fpga_version()・・・FPGAに埋め込まれたバージョン番号を読みだします。バージョン番号は16進数8桁で、最初の6桁はFPGAが作られた年月日になっていて、最後の2桁はその日の中の改版番号です。
- csz_xadc_tempe()・・・FPGA内の内蔵ADCで測ったFPGAの温度を返します。
コンパイル
プログラムをビルドするには、Visual Studioのx64 Native Tools Command Prompt
を起動して
cl test.cpp libcsz.lib
と入力してください。CLのオプションにlibcsz.libを付けることで、libcsz.dllを読み込めるようになります。
もしくはVisual Studioでプロジェクトを作成してビルドしてもよいでしょう。libcsz.dllは64bitなので、プロジェクトは64bit環境でビルドしてください。
実行結果
test.exeというファイルが出来上がるので実行します。引数にはIPアドレスまたはCosmo-Zのホスト名(cosmozまたはcszmini)を指定します。
FPGAに埋め込まれたバージョン番号と現在のFPGA温度が表示されます。
D:\naitou\np1068\cszwin\x64\Debug>test.exe 192.168.2.6 Connect to 192.168.2.6 FPGA Version=23070908 Currnet temperature=60.290491
ADコンバータの値を読む
次にADコンバータの値を単純に読みだして表示するプログラムを作成します。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "cosmoz.h" int main (int argc, char *argv[]) { if(argc < 2) { printf("test.exe <hostname>.\n"); return 0; } if(csz_open(INTERFACE_TCPIP, argv[1]) == FALSE) { printf("Cosmoz driver open failed.\n"); return 0; } for(int ch=0;ch<4;ch++) { printf("CH%d\t",ch + 1); } printf("\n"); for(int ch=0;ch<4;ch++) { printf("%d\t",csz_adc_val(ch + 1)); } printf("\n"); csz_close(); }
このプログラムではcsz_adc_val(ch)という関数を使用しています。この関数は指定されたチャネルのADCの値を読むことができます。
前のプログラムと同様に
cl test.cpp libcsz.lib
でコンパイルします。
実行結果は次のとおりです。
D:\naitou\np1068\cszwin\x64\Debug>test.exe 192.168.2.6 CH1 CH2 CH3 CH4 8580 8703 6784 12131
ADC値の換算方法
csz_adc_valで返される値は生のADC値です。ADCが12bitの場合は0~4095、14bitの場合は0~16383、16bitの場合は0~65535となります。これを電圧に換算するには、
計測電圧 = (ADC値/分解能) * (最大電圧 - 最小電圧) + 最小電圧
とします。
また、標準のハードウェアでは最大電圧は通常は0.5、最小電圧は通常は-0.5です。電圧レンジを変更している場合はこの限りではありません。
ADC値と同時に電圧を表示するように改良したプログラムの例を示します。
uint32_t adcval[4]; for(int ch=0;ch < 4;ch++) { adcval[ch] = csz_adc_val(ch + 1); } for(int ch=0;ch < 4;ch++) { printf("%d\t",adcval[ch]); } printf(" [ADC]\n"); for(int ch=0;ch < 4;ch++) { printf("%.3f\t",adcval[ch] / 16384. - 0.5); } printf(" [V] \n");
実行結果は以下のようになります。
D:\naitou\np1068\cszwin\x64\Debug>test.exe 192.168.2.6 8592 8705 10059 9850 [ADC] 0.024 0.031 0.114 0.101 [V]
連続同時サンプリング
csz_adc_val()は、関数を呼び出した時点でADCの値をサンプリングしているため、複数チャネルの同時サンプリングはできません。適当なタイミングでサンプリングされるので、サンプリングされた時刻もわかりません。
同時サンプリングを行うにはcsz_capture_execute関数を使用します。データの取り出しにはcsz_capture_datacopy関数を使用します。
以下にサンプルプログラムを示します。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "cosmoz.h" int main (int argc, char *argv[]) { if(argc < 2) { printf("test.exe <hostname>.\n"); return 0; } if(csz_open(INTERFACE_TCPIP, argv[1]) == FALSE) { printf("Cosmoz driver open failed.\n"); return 0; } csz_adc_freq(80); usleep(100000); // ADC周波数を変えた場合は30ms以上のWaitが必要 const int DATASIZE=10; CAPTURE_INFO info; info = csz_capture_execute(0xff,DATASIZE,CAPTRIG_AUTO,1); if(info.result != CAPRESULT_SUCCESS) { printf("キャプチャエラー\n"); csz_close(); return -1; } printf("time[ns]\tCH1\tCH2\tCH3\tCH4\n"); uint16_t *data1 = new uint16_t[DATASIZE]; uint16_t *data2 = new uint16_t[DATASIZE]; uint16_t *data3 = new uint16_t[DATASIZE]; uint16_t *data4 = new uint16_t[DATASIZE]; csz_capture_datacopy(info,1,data1); // CH1を読む csz_capture_datacopy(info,2,data2); // CH2を読む csz_capture_datacopy(info,3,data3); // CH3を読む csz_capture_datacopy(info,4,data4); // CH4を読む for(int i=0;i < DATASIZE;i++) { printf("%0.1f\t%d\t%d\t%d\t%d\n",i * 12.5,data1[i],data2[i],data3[i],data4[i]); } delete[] data1; delete[] data2; delete[] data3; delete[] data4; csz_close(); }
ADC周波数の設定
csz_adc_freq(80)はADコンバータのサンプリング周波数を80MHzに設定します。単位はMHzで、設定可能な値は1,2,4,8,10,16,20,25,40,50,80,100,125です。
この関数を呼び出してサンプリング周波数を変更した後、ADCが正しいデータを送ってくるようになるまで100msほど待ってください。
キャプチャ関数の説明
csz_capture_execute はキャプチャを実行する関数です。
プロトタイプ宣言は、
CSZAPI CAPTURE_INFO CSZCALL csz_capture_execute( uint32_t chmask, int length, CAPTURE_TRIGGER type, int maxseq);
です。
第1引数のchmaskは、どのチャネルをサンプリングするかをビットマスクで指定します。例えば0x15にした場合、二進数で00010101なので、CH1、CH3、CH5をキャプチャします。
第2引数のlengthはキャプチャしたい長さです。10,000,000くらいまでの値を指定することが可能ですが、キャプチャしたデータのサイズは (チャネル数×長さ×2) Byte になります。512MByteを超えないようにしてください。
第3引数のCAPTURE_TRIGGER typeには、CAPTRIG_AUTO か CAPTRIG_NORMAL を指定します。CAPTRIG_AUTOはトリガがなくてもキャプチャを行います。CAPTRIG_NORMALはトリガが入るまでキャプチャの開始を待ちます。
第4引数のmaxseqはシーケンシャルモードで使います。通常は0または1を指定します。(0も1もどちらも同じ意味で、シーケンシャルモードを使わないことを意味します。)
実行結果はCAPTURE_INFO型の構造体で返されます。CAPTURE_INFO構造体には以下に示すような情報が格納されています。
型 | 変数名 | 機能 |
---|---|---|
CAPTURE_RESULT | result |
キャプチャ実行結果 |
uint32_t | chmask | キャプチャしたチャネル |
int | chcount | キャプチャしたチャネル数 |
int | length | キャプチャ長 |
int | iteration | 繰り返し回数 |
int | maxseq | 最大シーケンス長 |
CAPTURE_TRIGGER | trig |
キャプチャトリガの種類 |
time_t | start_time | 開始時刻 |
time_t | end_time | 終了時刻 |
double | sampling_rate | サンプリングレート |
int | adc_freq | 実行開始時のADC周波数 |
int | adc_div | 実行開始時のデシメーション比 |
int | resolution | 分解能(bit単位) |
uint32_t | gps_time | 実行開始時のGPS時刻 |
uint32_t | gps_10ns | 実行開始時のGPS時刻 10ns単位 |
uint32_t | gps_maxcount | 実行開始時のGPS最大カウント |
uint64_t | start_time48 | 実行開始時の48bitタイムスタンプ |
uint32_t | top_addr | 格納される物理アドレスの先頭 |
uint32_t | bot_addr | 格納される物理アドレスの最後尾 |
uint32_t | last_addr | 格納された物理アドレスのlast |
int | mesp | 現在読み出し中のデータのポインタ |
int | evcount | 現在読み出し中のデータの数 |
uint32_t | prev_capaddr | イベントキャプチャのデータをここまで読んだポインタ |
uint32_t | start_addr | イベントキャプチャのデータが入っている先頭アドレス |
RESULTは実行結果で、CAPRESULT_SUCCESSを返した場合はキャプチャは成功です。
この構造体を参照することで、キャプチャ時のADC分解能やサンプリングレート、開始時刻、データ長などを知ることができます。
実行結果
実行結果を示します。最初の列は時刻、2番目の列はCH1、3番目の列はCH2、4番目の列はCH3、5番目の列はCH4のデータとなります。得られたADCの値を電圧に変換するにはADCの分解能の情報が必要ですが、(1 << info.resulution)-1 で取得することができます。
D:\naitou\np1068\cszwin\x64\Debug>test.exe 192.168.2.6 time[ns] CH1 CH2 CH3 CH4 0.0 8596 8711 5085 6058 12.5 8598 8710 5066 6083 25.0 8602 8711 5054 6103 37.5 8603 8717 5042 6133 50.0 8605 8711 5029 6162 62.5 8601 8711 5013 6189 75.0 8601 8710 5007 6213 87.5 8600 8712 4994 6246 100.0 8600 8710 4979 6269 112.5 8600 8711 4971 6290
コマンドを実行する際に、
test.exe > result.txt
としてテキストファイルに出力して、それをWindowsのエクスプローラで開いてテキストエディタで開いてコピーペーストして、Excelに貼り付ければ波形をグラフ化できます。