見出し画像

DelphiでFT232Hを使ってみる(2)

前回(DelphiでFT232Hを使ってみる(1))はデバイスの情報を入手するまででしたが、今回は本格的に動作をさせていきます。

3.Bit Bang Modeを動かしてみる

bit単位で制御ができるモードのようです。
例えばSC1602などのLCDモジュールを、I2C(PCF8574)を介さずにパラレルで制御することが可能になります。
FT232Hのポート毎に入出力を設定できますが、わかりやすくするためにD7-D4を出力に、D3-D0を入力に設定してみます。

//ModeとPortの設定
  Port:=$F0;
  Res:=Set_USB_Device_BitMode(Port,$1);  //asynchronous bit-bang mode
  if Res=0 then
    Memo2.Lines.Add('Portを'+inttohex(Port,2)+'に設定:'+#13#10+'  D7-4は出力、D3-0は入力')
    else
    Memo2.Lines.Add('エラー');

この状態で、$50を送信してみます。なお、D3-D0は入力に設定されていますので、0でもFでも何を設定しても関係はありません。

//送信データの設定
  TX_count:=1;
  TX_data:=$50;
//TX
  FT_Out_Buffer[0]:=TX_data;
  Res:=Write_USB_Device_Buffer(TX_count);
  if Res=TX_count then
    Memo2.Lines.Add(inttohex(TX_data,2)+'を設定')
    else
    Memo2.Lines.Add('TX Error');

そして、FT232Hのポートの状態を読み込んでみます。

//RX
  Res:=Get_USB_Device_BitMode(RX_data);
  if Res=0 then
    Memo2.Lines.Add(inttohex(RX_data,2)+'を読み込み')
    else
    Memo2.Lines.Add('RX Error');

すると入力ポートD3-D0の結果が想定外となっています。

読み込み結果

期待値は$50ですが$5Fとなっていますので、テスターで電圧を確認してみると、約2.5Vとなっています。入力なので不定状態になっていると思われ、プルダウン抵抗を付けてみると、期待値が得られるようになりました。

D3にプルダウン抵抗追加

後は、TX_dataの値を変化させていけば良いことになります。
例えば、$80と$00を交互に出力させればD7をSerial Clockとして使うことができます。

  TX_data:=$80;
  for i := 0 to 100 do
    begin
      FT_Out_Buffer[0]:=TX_data;
      Res:=Write_USB_Device_Buffer(TX_count);
      if TX_data=$80 then TX_data:=$00
        else TX_data:=$80;
      sleep(100);
    end;

テスターしか持っていないのでテスターの応答速度を考慮したウエイト時間(例は100msec)が必要ですが、電圧が変化している事が確認できました。
ただ、BaudRateの設定がどのように影響するのか、確認はできませんでした。BaudRateに応じた速度で出力されるようですが、オシロかロジアナが欲しいところです。

4.MPSSEを動かしてみる

I2C/SPI/JTAGをコマンドで制御できるモードのようです。
PCF8574であればI2CでSlave Addressと設定データを送るだけなので簡単にできるかと思い、下記の構成でトライしてみました。
なお、SCL,SDAにはプルアップ抵抗を追加しています。

システム構成

最初は簡単にできると思っていて、コマンドらしきものを探しました。FTDI社のアプリケーションノートAN177にそれっぽいのがあったのですが、多分ですがLibMPSSE.dllを使う必要があるのではないでしょうか。その為のユニット(*.pas)は提供されていないようなので、断念しました(Delphiの残念な部分?)。

そこで、FTDI社のアプリケーションノートAN108、255等を、またネット上の情報を参考にさせていただいて、何とかPCF8574のポートを設定できるようにはなりましたが、一言で言って超面倒くさいです。途中で挫折しそうになりました。
I2Cのルールに従う必要があり、スタートコンディションを設定、アドレスを設定、Ackの受信(実際は無視していますが)、最後はストップコンディションを設定、と一連の処理が必要です。
ポート設定のためだけにこれだけのコードが必要になるかと思うと使う意欲が失せてしまったというのが本音です。
サブルーチン化すればもう少し見やすくなるでしょうし、受信もさせなければいけないとは思いますが、それは次の機会にしたいと思います。

procedure TFrmMain.btnMPSSEClick(Sender: TObject);
var
  Res:Byte;
  SetData:Byte;
  SetAddress:Byte;
begin
  Memo3.Clear;
//Device Open
  Open_USB_Device;
  Reset_USB_Device;

//Modeの設定
  Set_USB_Device_LatencyTimer(16);
  Set_USB_Device_Timeouts(1000,1000);

  Set_USB_Device_BitMode($00,$00); //Reset
  Res:=Set_USB_Device_BitMode($00,$02);  //MPSSE mode

  if Res=0 then
    Memo3.Lines.Add('MPSSEに設定')
    else
    Memo2.Lines.Add('エラー');

//I2Cの設定
  SetAddress:=$4E;   //PCF8574のAddress
  SetData:=$00;      //ポートの設定
  OutIndex:=0;

//Set Command
  AddToBuffer($85);  //LoopBack
  AddToBuffer($97);  //Disable adaptive clocking
  AddToBuffer($8C);  //Enables 3 phase data clocking

  AddToBuffer($86);  //set clock divisor
  AddToBuffer($C8);  //value L  AN255 Page8 @100kHz
  AddToBuffer($00);  //value H

  SendBytes(OutIndex); // send
  OutIndex := 0;   //念のため

//Set Low Byte
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($03);  //SDA,SCL=H
  AddToBuffer($FF);  //SDA,SCL:Output
//Set High Byte
  AddToBuffer($82);  //Set High Byte
  AddToBuffer($00);  //Value:L
  AddToBuffer($FF);  //Direction:Output

  SendBytes(OutIndex); // send
  OutIndex := 0;

//Start Condition
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($03);  //SDA,SCL=H
  AddToBuffer($FF);  //SDA,SCL:Output
  //SDA=L
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($01);  //SDA=L,SCL=H
  AddToBuffer($FF);  //SDA,SCL:Output
  //SCLをLow
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($00);  //SDA,SCL=L
  AddToBuffer($FF);  //SDA,SCL:Output

  SendBytes(OutIndex); // send
  OutIndex := 0;

//Address送信
  AddToBuffer($11);  //Bytes Out
  AddToBuffer($00);  //Length L
  AddToBuffer($00);  //Length H
  AddToBuffer(SetAddress);  //Address
  //SCLをLow
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($00);  //SDA,SCL=L
  AddToBuffer($FD);  //SDA:Hi-Z
  //SDAをHi-Z
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($02);  //SDA=H,SCL=L
  AddToBuffer($FD);  //SDA:Hi-Z
  //SCLをHigh
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($01);  //SDA=L,SCL=H
  AddToBuffer($FD);  //SDA:Hi-Z
  //SCLをLow
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($02);  //SDA=H,SCL=L
  AddToBuffer($FF);  //SDA,SCL:Output

  SendBytes(OutIndex); // send
  OutIndex := 0;

//データを送信
  AddToBuffer($11);  //Bytes Out
  AddToBuffer($00);  //Length L
  AddToBuffer($00);  //Length H
  AddToBuffer(SetData);  //Data
  //SDA,SCLをLow
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($00);  //SDA,SCL=L
  AddToBuffer($FF);  //SDA,SCL:Output
  //SDAをHi-Z
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($02);  //SDA=H,SCL=L
  AddToBuffer($FD);  //SDA:Hi-Z
  //SCLをHigh
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($01);  //SDA=L,SCL=H
  AddToBuffer($FD);  //SDA:Hi-Z
  //SCLをLow
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($02);  //SDA=H,SCL=L
  AddToBuffer($FF);  //SDA,SCL:Output

  SendBytes(OutIndex); // send
  OutIndex := 0;

//STOPコンディション
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($00);  //SDA,SCL=L
  AddToBuffer($FF);  //SDA,SCL:Output
  //SCLをHigh
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($01);  //SCL=H
  AddToBuffer($FF);  //
  //SDAをHigh
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($03);  //SDA,SCL=H
  AddToBuffer($FF);  //
  //SCLをHigh、SDAをHi-Z
  AddToBuffer($80);  //Set Low Byte
  AddToBuffer($01);  //SDA=L,SCL=H
  AddToBuffer($FD);  //SDA:Hi-Z

  SendBytes(OutIndex); // send
  OutIndex := 0;

  Close_USB_Device;

end;

実際の使用にあたっては、温度などを考慮してウエイトを入れたりする必要があるかと思われます。それも含めて、まずはI2Cの波形をオシロなりロジアナで確認して、次のSTEPに進みたいと考えています。

5.まとめ

なんとかBit Bang ModeとMPSSEを動かしてみましたが、FT232Hは上級者向けと感じました。
JTAGや8bitパラレルで使いたいのであればFT232Hを選択せざるえないでしょうが、I2CやSPIを使いたいのであればエレファイン様のISSで十分だと思います。こちらの方が簡単に動かせて、初級者には優しいと言えます。

ただ、Delphiを使っているからそう感じたのであり、C++とかであればもっと情報があるでしょうから、簡単に使えるのかもしれません。

いいなと思ったら応援しよう!