[C#]SerialPortを使わず「FTD2XX_NET」でFTDIのUSBシリアル通信してみる

FTDIのUSBチップ(FT232RやFT232Hなど)は、USBとシリアル通信を変換する定番ICで、これが載った機器を使うことが結構あったりします。PCと通信する際、.Netや.NetFrameworkでは「COM3」「COM4」とかCOMポート番号を指定してSerialPortクラスを使うのが普通ですが、COMポート番号はUSBの差込口を変えたり、接続したタイミングによってPC毎に変わってしまうことも良くあります。そこで、今回はSerialPortクラスは使用せず、「FTD2XX_NET」を使ってFTDIのシリアル番号を指定して通信する方法を試してみたいと思います。

VisualStudioで.NetのWindowsフォーム、C#での説明になります。まず、まっさらのForm1だけの状態で、次のFTDIの公式サイトから「FTD2XX_NET.dll」をダウンロードし、参照に追加します。

フォームにボタン「GetSerials_button」とリストボックス「Serials_listBox」を追加し、ボタンのクリックイベントにFTDIのシリアル番号を取得するプログラムを書きます。

using FTD2XX_NET;

namespace WinFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void GetSerials_button_Click(object sender, EventArgs e)
        {
            FTDI ftdi = new FTDI();
            uint devcount = 0;
            ftdi.GetNumberOfDevices(ref devcount);

            FTDI.FT_DEVICE_INFO_NODE[] ftList = new FTDI.FT_DEVICE_INFO_NODE[devcount];
            ftdi.GetDeviceList(ftList);

            Serials_listBox.Items.Clear();
            for (int i = 0; i < ftList.Length; i++)
            {
                Serials_listBox.Items.Add(ftList[i].SerialNumber);
            }
        }
    }
}

一旦これで実行してみます。ボタンを押すとPCに接続されたFTDIの機器のシリアルナンバーが表示されるようになったかと思います。(ちなみに、シリアルナンバーを調べる方法は「FT Prog」というFTDI公式サイトからダウンロードできるソフトを使う方法もあります。)

次にOpen_buttonとClose_buttonを追加し、次のようにします。リストで選択したシリアルで接続と切断ができるようになります。さらに、送受信ボタン、送受信文字列用のテキストボックスを用意し次のようにします。文字列通信の区切り文字は送受信ともに”\r\n”ということにします。

using FTD2XX_NET;
using System.Diagnostics;
using System.Text;

namespace WinFormsApp1
{
    public partial class Form1 : Form
    {
        FTDI ftdi;

        public Form1()
        {
            InitializeComponent();
            ftdi = new FTDI();
        }

        private void GetSerials_button_Click(object sender, EventArgs e)
        {
            FTDI ftdi = new FTDI();
            uint devcount = 0;
            ftdi.GetNumberOfDevices(ref devcount);

            FTDI.FT_DEVICE_INFO_NODE[] ftList = new FTDI.FT_DEVICE_INFO_NODE[devcount];
            ftdi.GetDeviceList(ftList);

            Serials_listBox.Items.Clear();
            for (int i = 0; i < ftList.Length; i++)
            {
                Serials_listBox.Items.Add(ftList[i].SerialNumber);
            }
        }

        private void Open_button_Click(object sender, EventArgs e)
        {
            if (ftdi.IsOpen)
            {
                return;
            }

            var serial = Serials_listBox.SelectedItem;

            if (serial != null)
            {
                FTDI.FT_STATUS ftStatus = ftdi.OpenBySerialNumber(serial.ToString());
                if (ftStatus != FTDI.FT_STATUS.FT_OK)
                {
                    throw new Exception("FTDI Open Error:" + ftStatus.ToString());
                }

                ftdi.SetBaudRate(115200);                 //ボーレート
                ftdi.SetDataCharacteristics(
                       FTDI.FT_DATA_BITS.FT_BITS_8,       //データビット 8bit
                       FTDI.FT_STOP_BITS.FT_STOP_BITS_1,  //ストップビット 1bit
                       FTDI.FT_PARITY.FT_PARITY_NONE);    //パリティ none
                ftdi.SetLatency(1);                       //MBオプション 待ち時間msec(デフォルト値は16)
            }
        }

        private void Close_button_Click(object sender, EventArgs e)
        {
            ftdi.Close();
        }

        private void Send_button_Click(object sender, EventArgs e)
        {
            string SendString = SendStr_textBox.Text + "\r\n";
            uint Written = 0;
            FTDI.FT_STATUS ftStatus = ftdi.Write(SendString, SendString.Length, ref Written);

            if (ftStatus != FTDI.FT_STATUS.FT_OK)
            {
                throw new Exception("FTDI Write Status Error");
            }
        }

        private void Recieve_button_Click(object sender, EventArgs e)
        {
            RecieveStr_textBox.Text = string.Empty;

            long timeOut = 500;     //受信タイムアウトms
            Stopwatch sw = Stopwatch.StartNew();
            StringBuilder rBuilder = new StringBuilder();

            while (sw.ElapsedMilliseconds < timeOut)
            {
                uint ReadAb = 0;
                ftdi.GetRxBytesAvailable(ref ReadAb);
                if (ReadAb > 0)
                {
                    uint ReadBy = 0;

                    FTDI.FT_STATUS ftStatus = ftdi.Read(out string rStr, ReadAb, ref ReadBy);
                    if (ftStatus == FTDI.FT_STATUS.FT_OK)
                    {
                        rBuilder.Append(rStr);
                        //末尾が\r\nなら受信確定して終了  \r\nでなければ受信を継続
                        if (rStr.EndsWith("\r\n"))
                        {
                            break;
                        }
                    }
                    else
                    {
                        throw new Exception("FTDI Read Status Error");
                    }
                }
            }
            RecieveStr_textBox.Text = rBuilder.ToString();
        }
    }
}

これで、接続、切断、送信、受信、一通りできるようになったかと思います。今回の例では、簡単に理解できるようにForm1に直接コードを記述しています。実際は環境にあわせて記述してください。

一方、FTDI側の機器についてですが、実験で通信相手として用意したのは、FTDIの載ったArduinoNano(正規版)。ArduinoNanoは説明をしやすくするために用意したものであって、テキスト通信する機器であれば、実際は別の機器を使ってもらっても良いと思います。(ただ、ものによっては、EEPROM内臓されておらずFTDIのシリアル番号が登録されていないものもある様なのでその場合はこの方法はとれないかもしれません。)


Arduinoのプログラムは、今回のテストではシリアル通信で受信した文字列の頭にOKをつけて応答を返すだけのプログラムにしました。

//Arduino テストプログラム
//シリアル通信で受信した文字列に対して
//頭にOKをつけて応答を返すだけのプログラム

void setup() {
  Serial.begin(115200);
}

void loop() {
  String rstr = RecieveCommand();

  if(rstr != "") {
    Serial.print("OK" + rstr + "\r\n");
  }
}

String RecieveCommand() {
  String rstr = "";

  if(Serial.available())
  {
    rstr = Serial.readStringUntil('\n');
  
    if(rstr != "")
    {
      rstr.trim();
    }  
  }
  return rstr;
}

コメント