[KEKB Bunch Feedback Group]

CAEN V1718用Delphi XE3 インターフェースライブラリ


by とびやま まこと(Makoto Tobiyama)/KEKB ビームモニターグループ

警告
以下の記述に関しては、意図する、しないに関わらず悪意に基づく多くの誤り、誤解が含まれていると思われますので、 決して信用してはいけません。これを信じて起きた損害に関しては、当方は一切責任を持ちません。


If you want to contact with the author, please E-mail to makoto.tobiyama@kek.jp.
目次

1.はじめに

CAEN社V1718は、VME64xに対応した1幅6U高のVMEマスターで、USB2.0インターフェースを通して PCから制御出来ます。詳しい説明はここ をご参照下さい。
V1718にはライブラリ(Windows用、Linux用)、サンプルコード(同じく)が付いてきますが、 これだけで済まないときは、自分でライブラリにアクセスして制御するコードを書く必要があります。本欄では、 Embacardero Delphi XE3 (XE2でも)を使ってV1718を制御する方法を説明します。なお、 最低限必要な機能のみをimplementしただけなので、それ以上の機能が必要な方は、ご自分で御工夫お願い致します。

2.PC環境

はじめにPCにCAEN社のライブラリをインストールする必要がありますが、 インストールする必要があります。逆はうまくいきません。また、何らかの 理由でうまくインストール出来なかったときは、心を入れ替えてやり直してもダメなことが あるようです(未だに1台のWindowsXPマシンはインストールできない状態です)。
32ビットOSでは、全て32ビットで統一するしか他にやりようがないと思われますが、 64ビットOSでは、少なくともデバッグは64ビットターゲットでしか出来ません。私の 開発環境は
で、自マシンでの動作、及び他のWindows7 Pro 64bitマシンでのexeファイルの動作 は確認していますが、32bit OSマシンでの試験はしていません。32ビットコードが ちゃんと出来るかも確認出来ていません。

3.Delphi側の構成

RAD studio XE3側の設定は64ビット版の時のTeeChart設定を除き必要ありません。ライブラリがちゃんと インストール出来ていれば、souceディレクトリにDLLファイルがなくても大丈夫な 気もしますが、念のため
CAENVMElib.dll
があるようにして下さい。これを使うDelphi側のインターフェースユニットは です。
CAENのアクセスとは全く関係ありませんが、64bit開発環境で、TeeChart(御本格版)をインストールして使用する場合、 TeeChartのライブラリパスがインストールしたままの設定ではコンパイル時にエラーが出て使用出来ません(XE2でも XE3でも)。ツール-オプションで、64ビットWindowsを選び、ライブラリパスのなかにある、TeeChart 2012 for RAD XE3\Delphi17.win64\libを一番上になるように持ってきます。(ついでに、灰色はパスが存在しないことを表して います。というコメントを信じて不正なパスを削除、なんてするとエライ目にあいます。ご用心を)。


4.初期設定

FormCreateなどで、V1718との通信設定を行います。
procedure TVMEF.FormCreate(Sender: TObject);
var i,rtn : integer;
    link,un : shortint;
begin
  un := 0;
  link := 0;
  rtn := vme_init(cvV1718,link,un,Handle1);
  if rtn <> cvSucess then
   showmessage('Device Error');
end;

ここで、Handle1はglobal変数で、
var 
    Handle1 : longint;
という風に定義してあります。

5.通常のread/write

readの時は、VMEアドレス、戻り値変数、AMコードを与えて、
 vme_readcycle(Handle, vme address, 戻り値, AMコード, データ幅)
を呼びます。たとえば、A32特権データアクセスをするときは、
procedure TVMEF.BtnReadClick(Sender: TObject);
var rtn : integer;
    rdata : cardinal;
    am : byte;
    s : string;
begin

  address := strtoint('$'+Eaddress.Text);
  am := cvA32_S_DATA ;
  case
    Raddatawidth.ItemIndex of
    0 : begin
          rtn := vme_readcycle(Handle1,address,rdata,am,cvD8);
          if rtn <> cvSucess then begin
            s := inttostr(rtn);
            showmessage('Error '+s);
            end;
        end;    
    1 : begin
          rtn := vme_readcycle(Handle1,address,rdata,am,cvD16);
          if rtn <> cvSucess then begin
            s := inttostr(trn);
            shwomessage('Error '+s);
            end;
        end;    
    2 : begin
          rtn := vme_readcycle(Handle1,address,rdata,am,cvD32);
          if rtn <> cvSucess then begin
            s := inttostr(rtn);
           showmessage('Error '+s);
           end;
    end;
  end;
  labdataread.Caption := system.SysUtils.inttohex(rdata,8);
end;
のような使い方になります。writeの時も同様で、
procedure TVMEF.BtnWriteClick(Sender: TObject);
var i, rtn : integer;
    s : string;
    wdata : cardinal;
    am : byte;
begin
  address := strtoint('$'+Eaddress.Text);
  wdata := strtoint('$'+Edata.Text);
  am := byte(cvA32_S_DATA) ;
  case
    Raddatawidth.ItemIndex of
    0 : rtn := vme_writecycle(Handle1,address,wdata,am,cvD8);
    1 : rtn := vme_writecycle(Handle1,address,wdata,am,cvD16);
    2 : begin
          rtn := vme_writecycle(Handle1,address,wdata,am,cvD32);
          if rtn <> cvSucess then begin
            s := inttostr(rtn);
           showmessage('Error '+s);
          i := rtn;
           end;
    end;
  end;
end;
みたいにすれば動きます。なお、V1718の単発read/writeは多分USB部分から来る ものすごいオーバーヘッドがあるようで、大きな(という程でなくても)データを 読み書きすると結構な時間がかかります。可能であれば、以下で説明する ブロック転送を使った方がずっと早いです。もちろん、ボード側がブロック転送 に対応していなければダメですが。

6.ブロック転送(BLT/MBLT)

現在のところ、V1718はBLT(32ビットデータ幅ブロック転送)とMBLT(64ビットデータ幅 ブロック転送)をサポートしています。BLTの場合
function VME_BLTReadCycle(Handle: longint; Address : longword; p: pointer; Size: integer; AM: integer; DW : integer; var count : integer): integer; stdcall;
function VME_BLTWriteCycle(Handle : longint; Address : longword; p: pointer; Size : integer; AM : integer; DW: integer; var count : integer) : integer; stdcall;
を、MBLTの場合
function VME_MBLTReadCycle(Handle : longint; Address : longword; p: pointer; size : integer; AM : integer; var count : integer): integer; stdcall;
function VME_MBLTWriteCycle(Handle: longint; Address : longword; p: pointer; Size : integer; AM : integer; var count : integer):integer; stdcall;
を使うことになります。
BLTでもMBLTでも基本的な使い方は同じです。VME64x規格上、ブロック転送は256回まで となっていますが、ユーザー側がこれを意識して制御する必要はありません。例えば、 MBLTでデータを書き込む時は
procedure TVMEF.BtnMBLTWriteClick(Sender: TObject);
var rtn : integer;
    i : integer;
    add,rdata : cardinal;

    am : byte;
    size,count : integer;
    p : pointer;

    s : string;
begin
  address := strtoint('$'+Eaddress.Text);
  for i := 0 to memsize -1 do rd[i] := 0;

  size := memsize*8;  {データサイズ with byte}
  am := cvA32_S_MBLT;
  p := @rd[0];

  rtn := VME_Mbltwritecycle(handle1,address,p,size,am,count);
          if rtn <> cvSucess then begin
            s := inttostr(rtn);
           showmessage('Error '+s);
           end;
end;
の様にしますし、MBLTでデータを読み込むときは
procedure TVMEF.BtnMBLTreadClick(Sender: TObject);
var rtn : integer;
    i,j : integer;
    add,rdata : cardinal;

    am : byte;
    size,count : integer;
    p : pointer;

    s : string;
begin
  address := strtoint('$'+Eaddress.Text);
  size := memsize*8;
  am := cvA32_S_MBLT;
   
  begin
     p := @rd[0];
     rtn := VME_Mbltreadcycle(handle1,address,p,size,am,count);
          if rtn <> cvSucess then begin
            s := inttostr(rtn);
            showmessage('Error '+s);
           end;

  for i := 0 to memsize-1 do
   begin
     ch1[i] := rd[i] and $ffff;
     ch3[i] := (rd[i] shr 16) and $ffff;
     ch2[i] := (rd[i] shr 32) and $ffff;
     ch4[i] := (rd[i] shr 48) and $ffff;
   end;
  end;
 viewdataf.Series1.Clear;
 viewdataf.Series2.Clear;
 viewdataf.Series3.Clear;
 viewdataf.Series4.Clear;
   for i := 0 to memsize-1 do
   begin
     viewdataf.Series1.AddXY(i,ch1[i],'',clTeeColor);
     viewdataf.series2.addxy(i,ch3[i],'',clTeeColor);
     viewdataf.Series3.AddXY(i,ch2[i],'',clTeeColor);
     viewdataf.series4.addxy(i,ch4[i],'',clTeeColor);

   end;
   viewdataf.Show;

end;
とかいった感じになります。ここでrdとかch1..ch4は
var 
    rd : array[0..262143] of uint64;
    ch1,ch2,ch3,ch4 : array[0..262143] of word;
のように定義してあります。もちろん、書き込んだり、読み込んだりする データ構造によって、コードの中身はすっかり変わるはずですが。
なお、BLT転送を行っている最中、規格上は必ずしもVMEアドレスを出す 必要は無いと思われますが、V1718は几帳面にアドレスを出しています。 これに対して誤動作するようなボードは(そもそもBLTの規格に合って いないが)使えません。

7.割り込み動作

V1718はVME割り込みにも対応しています。割り込み機能をenableするには
  cmd := irqset;
  rtn := VME_IRQEnable(handle1, cmd);
を使います。ここで、irqsetはタダの割り込み番号ではダメで、
procedure TVMEF.RadIRQClick(Sender: TObject);
begin
  case radIRQ.ItemIndex of
    1: irqset := cvIRQ1;
    2: irqset := cvIRQ2;
    3: irqset := cvIRQ3;
    4: irqset := cvIRQ4;
    5: irqset := cvIRQ5;
    6: irqset := cvIRQ6;
    7: irqset := cvIRQ7;

  end;
end;
のように、cvIRQ1からcvIRQ7までのどれかである必要があります。 もちろん、決めうちでもかまいません。
割り込み番号、vectorは例えば以下の様に確認することが出来ます。
procedure TVMEF.BtnIRQCheckClick(Sender: TObject);
var rtn : integer;
    mask ,lv: byte;
    s : string;
begin
   rtn := vme_irqcheck(handle1,mask);
   rtn := vme_iackcycle(handle1,mask,lv,cvD8);
   labdataread.Caption := 'IRQ'+inttohex(mask,2)+' '+inttohex(lv,2);
end;
具体的な割り込み機能を使ったprocedureとしては、 といった感じになります。割り込みサイクルを止めるときは、 といった操作になると思います。以下にtimerの例を示します。
procedure TVMEF.Timer1Timer(Sender: TObject);
var i,j : longint;
    rtn : integer;
    cmd : cardinal;
    vector : byte;
    size : integer;
    s : string;
    lv,lv1 : byte;
    rdata : cardinal;
    p : pointer;
    count : integer;
    sum1,sum2,sum3,sum4 : double;

begin
  base := strtoint('$'+Ebase.Text);
  size := memsize*8;
  rtn := vme_irqcheck(handle1,lv);
  lv1 := strtoint('$'+Evector.Text);

  if lv = irqset then
   begin
     rtn := vme_iackcycle(handle1,cvIRq4,lv,cvD8);
     if lv <> lv1 then
      begin
        labdataread.Caption := 'Invalid vecttor';
      end
     else
     labdataread.Caption := inttohex(lv,2);

     rtn := vme_readcycle(handle1,base+AD_STATUS,rdata,cvA32_S_DATA,cvD32);

     p := @rd[0];
     rtn := VME_Mbltreadcycle(handle1,base,p,size,cvA32_S_MBLT,count);
          if rtn <> cvSucess then begin
            s := inttostr(rtn);
           showmessage('Error '+s);
           end;
 
    cmd := 1;
    rtn := vme_writecycle(handle1,base+AD_start,cmd,cvA32_S_DATA ,cvD32);
   end;
  application.ProcessMessages;
end;

8.まとめ

V1718をRAD Studio Delphi XE2/XE3で使うインターフェースライブラリについて 紹介しました。V1718は値段もそれほど高くなく(でも安いとは言いたくない)、 WindowsでもLinuxでも動作しますし、それなりにちゃんとしたライブラリと (いまいちな)動作コードが付いてきますので、いまさらながらVME開発をせざるを えない方にとっては魅力的なVMEコントローラーと思われます。
KEKBでは、VMEは基本的にVxWorksで動作させますので、V1718をそのまま 加速器制御に直接使うことはありませんし、データ転送スピードなど(瞬間最高 速は別にして平均速度ではめちゃ遅)問題もあります。しかし、開発したボード をKEK外でデバッグするときなど、あほなマニュアルコントローラなどに比べ ずっと有効ですし、Delphiでコードを書くことにより、簡単に上等な 制御プログラムを作ることが出来ており、皆様にもお勧めします (と言って、Delphiにとりつかれた不幸な仲間を一人でも増やす企みかも)。
Makoto Tobiyama
9/May/2013

Return to FB Home Page...