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となっています。入力なので不定状態になっていると思われ、プルダウン抵抗を付けてみると、期待値が得られるようになりました。
後は、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++とかであればもっと情報があるでしょうから、簡単に使えるのかもしれません。