MATLABでFTDIデバイスの複数のポートを使う

##１．はじめに
昨今、データ通信の多くはシリアルになっており、デバイスへの入出力もI2Cなどが使われることが多くなっています。
ですが、一部のデバイスにおいて、８ビットマイコン（死語？）に接続することを前提としたようなものもあります。
この場合に、データバスに８ビットを割り振り、ＷＥ（ライトイネーブル）、ＲＥ（リードイネーブル）、ＣＳ（チップセレクト）といった制御信号を駆使してやり取りを行うことになります。

![Qiitaイラスト220924.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434947/8499b319-1437-6670-d3e9-24796683929f.png)

FTDI(Future Technology Device Internatinal Ltd.)社のUSB to パラレルのデバイス（モジュール）は、PCから安全にデジタル信号を出したり、取り込んだりするのに使われています。
本稿では、MATLABスクリプトにより、FTDIデバイスの２つ（または、それ以上）のポートを用いてデータの入出力を行う手法について、まとめてみました。

##２．既に公開されている資料について
MathWorks の MATLAB File Exchange にRoger M. Ellingson氏の投稿した [Test_FTD2XX_NET_BitBang.m](https://jp.mathworks.com/matlabcentral/fileexchange/28875-test_ftd2xx_net_bitbang) があります。これは、FTDI社が提供している .NET用のライブラリ FTD2XX_NET.dll を用いて、MATLABによりFTDIデバイスを操作するものです。
手順は
　１）まずFTDIのページからFTD2XX_NET.dll を[ダウンロード](https://ftdichip.com/software-examples/code-examples/csharp-examples/) します。
　２）dllファイルをPCのどこかわかりやすいところに置きます（ここではCドライブ直下に置いたとして話を進めます）。
　３）FTDIデバイスとPCをUSBケーブルで接続します。
　４）ここからはMATLAB上での作業となります。まず、dllの場所を指定します。

```matlab:xx.m
f = NET.addAssembly('C:\FTD2XX_NET.dll');
```
以下はTest_FTD2XX_NET_BitBang.m にあるので省略しますが、

```matlab:yy.m
methodsview( 'FTD2XX_NET.FTDI');
```
これを実行しますと、ファンクション一覧が表示されるので便利です。
（FTD2XX_NET.dllは正式なマニュアルがないらしいので、かなり参考になります）。

##３．複数のポートの使い方
Test_FTD2XX_NET_BitBang.m は１つのポートからデータを出力する場合、また、同じ作者の、[FTD2XX_NET_Write_GetPinStates](https://jp.mathworks.com/matlabcentral/fileexchange/31553-ftd2xx_net_write_getpinstates)は、１つのポートから入出力を行う例が示されています（読み込みにはReadでなくGetPinStates を使うことに注意）。
しかし、複数のポートを使う例が示されていません。

最も簡単な例は、ポートのインデックスを使うもので、

```matlab:cde.m
% dllの場所を指定する
f = NET.addAssembly('C:\FTD2XX_NET.dll');

% ２つのポートへのFTDIプロパティを割り当てる
f0=FTD2XX_NET.FTDI;
f1=FTD2XX_NET.FTDI;
pause(0.1);%準備ができるのを待つ

% ポートを開く
OpenByIndex(f0,0);
OpenByIndex(f1,1);

allBitsOutputMask = uint8(255); % '1' bit is output
bitBangMode = uint8(1); % '1' mode is async bitbang
resetMode = uint8(0);  % terminate bitbang
mixBitsMask = uint8(15); % '1'=output, d7-d=input, d3-d0=output

r = SetBitMode( f0,0,resetMode);
r = SetBitMode( f1,0,resetMode);

SetBaudRate(f0,115200);%ボーレート（転送レート）設定
SetBaudRate(f1,115200);%ボーレート（転送レート）設定

% 入出力指定　ポート0は全ビット出力、ポート１は下４ビットが出力
r = SetBitMode( f0, allBitsOutputMask, bitBangMode);
r = SetBitMode( f1, mixBitsMask, bitBangMode);

%%%  このあたりで、データの入出力を行う %%%

%ポートのクローズ
r = Close(f0);
r = Close(f1);

```
というようになります。

##４．ポートの指定方法
ポートの指定ですが、上記の方法はインデックスで指定しました。
インデックスでの指定は簡単で良いのですが、例えばFTDIデバイスを複数使うような場合には
どのデバイスのポートなのかわからなくなります（複数使う人は、それほどいるとは思いませんが）。
この場合に、ロケーションIDで指定する場合と、シリアルナンバーで指定する方法があります。
ロケーションIDで指定するにはロケーションIDがどうなっているかを調べる必要がありますが、そのためにはFTDIが用意しているユーティリティである[FT_Prog](https://ftdichip.com/utilities/#ft_prog)を使う必要があります。
FT_Progをダウンロードしてきて、起動します。
虫眼鏡アイコンをクリックします（FTDiデバイスは接続しておくこと）。

![FTProg1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434947/7fde83b7-51fd-0542-4194-a00dface03f5.png)

Property,Valueのところで、Location IDを見ると、'0x111' とあります。

![FTProg2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434947/e48514e8-a528-9e34-7af8-a2a67a6e59d1.png)


今、デバイスがFT2232Hで２ポートあるので、Location ID は'0x111' '0x112'の
２つとなります。
MATLAB でのスクリプトは、
```matlab:cde.m
% dllの場所を指定する
f = NET.addAssembly('C:\FTD2XX_NET.dll');

% ２つのポートへの　を割り当てる
f0=FTD2XX_NET.FTDI;
f1=FTD2XX_NET.FTDI;
pause(0.1);%準備ができるのを待つ

dwLoc0=hex2dec('111');
dwLoc1=hex2dec('112');

% ポートを開く
r = OpenByLocation(f0,dwLoc0);
r = OpenByLocation(f1,dwLoc1);

%%%  以下、同じです %%%

```
となります。
ロケーションIDで指定する場合には、PCのどのUSBコネクタに接続するかで、ロケーションIDが変わるため、注意が必要です（毎回同じところに挿せばよいという話も）。

どこに挿しても、特定のFTDIデバイスの特定のポートが指定されるようにしたい、という場合には、シリアルナンバーで指定する必要があります。
先ほどのFT_Progにもシリアルナンバーは表示されています。

![SerialNumber1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434947/f4d0acb1-bd1a-7f64-ef69-4dfb48a713cd.png)

今、ここでは'FT2OZ2EO'となっています。この場合に１つめのポートのシリアルナンバーは、
これに'A'がついて、'FT2OZ2EOA'、２つめのポートのシリアルナンバーは'B'がついて、'FT2OZ2EOB'となります。
MATLABでシリアルナンバーを取得して、２つのポートを用いて読み書きする例を以下に示します。

```matlab:serialport_open.m
f = NET.addAssembly('C:\FTD2XX_NET.dll');
f0=FTD2XX_NET.FTDI;

% 接続されているデバイスのch数がchsに入る
% FT4232の場合は4となる
% (fo,0)のゼロは便宜上入力
[r,chs]=GetNumberOfDevices(f0,0)

% シリアルナンバーを取得する
for t=0:chs-1
    % fn = FTD2XX_NET.FTDI;
    dum=['f' num2str(t) '=FTD2XX_NET.FTDI;'];
    eval(dum);
    
    %インデックス番号でポート接続
    % r = OpenByIndex(fn,n);
    dum=['r=OpenByIndex(f' num2str(t) ',' num2str(t) ');'];
    eval(dum);
    
    % シリアルナンバーを取得する
    % [r,sn] = GetSerialNumber(fn,0);
    dum=['[r,s' num2str(t) ']=GetSerialNumber(f' num2str(t) ');'];
    eval(dum);
    
    %ポートを閉じる
    % r = Close(fn);
    dum=['r=Close(f' num2str(t) ');'];
    eval(dum);
end

% これでs0,s1,...にシリアルナンバーが入っている

%%% s0 と s1 を使ってデータのやり取りをする

r = OpenBySerialNumber(f0,s0);
r = OpenBySerialNumber(f1,s1);

pause(0.1);

allBitsOutputMask = uint8(255); % '1' bit is output
bitBangMode = uint8(1); % '1' mode is async bitbang
resetMode = uint8(0);  % terminate bitbang
mixBitsMask = uint8(15); % '1'=output, d7-d=input, d3-d0=output

r = SetBitMode( f0,0,resetMode);
r = SetBitMode( f1,0,resetMode);

SetBaudRate(f0,115200);%ボーレート（転送レート）設定
SetBaudRate(f1,115200);%ボーレート（転送レート）設定

r = SetBitMode( f0, allBitsOutputMask, bitBangMode);
r = SetBitMode( f1, mixBitsMask, bitBangMode);

x = uint8([0,0,0]);%FTDIからポートに出力する値を入れる場所を用意

for n=0:5
    
    t=0;
    x(1) =uint8(t);%符号なし８ビットにする
    r = Write( f0, x, 1, 0);%書き出す AD0-7
    
    t=0;
    x(1) =uint8(t);%符号なし８ビットにする
    r = Write( f0, x, 1, 0);%書き出す

    m=0;
    x(1) =bitand(uint8(m),15);%符号なし８ビットにする
    r = Write( f1, x, 1, 0);%書き出す
    
    m=15;
    x(1) =bitand(uint8(m),15);%符号なし８ビットにする
    r = Write( f1, x, 1, 0);%書き出す



    [~,pinStates] = GetPinStates( f1, 0);% 4bit 読み込み
    bitshift(pinStates,-4)

end

r = Close(f0);
r = Close(f1);

```
ちなみに、秋月電子で入手できるFT2232Dですが、シリアルナンバーを記憶するためのメモリが実装されていません。ですので、シリアルナンバーは使えないはずですが、上記のスクリプトを実行させると、２つのポートは 'A' , 'B' と識別されます。FT2232Dを１つだけ使う分には問題ないかもしれません。

以上です。
お疲れさまでした。