■ USB通信(VC++)                


■ PIC18 CDCクラス Microchip社デモソフト

  PICとUSB通信を行なう場合、マイクロチップ社から提供(公開)されているいるUSBフレームワークのCDCクラスを使うPC側のソフトは
 サンプルソフトがマイクロチップ社のHPに公開されています。以下のプログラムは  マイクロチップ社のMCHPFSUSB Framework 2.3
 ダウンロードして解凍後PCにできる …\Microchip Solutions\USB Device - CDC - Basic Demo\PC Software Example\Vc++ 2005
 \Basic Communicationフォルダに収納されているVC++Cのサンプルプログラムです。
 このソフトは マイクロチップ  DemoBoard(PICDEM FS USB DM163025)($59.99ドル)用デモソフトのPC側ソフトです。 コメント行は
 翻訳、削除、追記等をしていますが 、ソースコードの変更は行なっていません。 全体のハード 及びPIC側のソフトは 全体&PIC側
 参照願います。 ドライバーをインストール後 API関数により以下のような通信が可能となります。

 
<プログラム>
Form1.h抜粋
 

#pragma once

namespace VCCDC {

        using namespace System;
        using namespace System::ComponentModel;
        using namespace System::Collections;
        using namespace System::Windows::Forms;
        using namespace System::Data;
        using namespace System::Drawing;
        using namespace System::Threading;


        public ref class Form1 : public System::Windows::Forms::Form
        {
        static int numPorts = 0;

        //受信スレッドから文字を取り込みtxtDataReceived textboxにデータを
        //書き込むデリゲート関数を宣言する。

        delegate void SetTextCallback(String^ text);

                public:
                
                Form1(void)     //コンストラクタ
                {
                        InitializeComponent();

                        UpdateCOMPortList();
                }

        
                ~Form1()        //デストラクタ
                {
                        if (components)
                        {
                                delete components;
                        }
                }

        protected: 
        private: System::IO::Ports::SerialPort^  serialPort1;
        private: System::Windows::Forms::Button^  btnConnect;
        private: System::Windows::Forms::Button^  btnSendData;
        private: System::Windows::Forms::TextBox^  txtSendData;
        private: System::Windows::Forms::Timer^  timer1;
        private: System::Windows::Forms::ComboBox^  lstCOMPorts;

        private: System::Windows::Forms::Button^  btnClose;
        private: System::Windows::Forms::TextBox^  txtDataReceived;
        private: System::ComponentModel::IContainer^  components;


#pragma region Windows Form Designer generated code
                /// <summary>
                //デザインに係る要求事項が記載されている。変更しないこと
                /// Required method for Designer support - do not modify
                /// the contents of this method with the code editor.
                /// </summary>
                void InitializeComponent(void)
                {
                        this->components = (gcnew System::ComponentModel::Container());
                        this->txtDataReceived = (gcnew System::Windows::Forms::TextBox());
                        this->serialPort1 = (gcnew System::IO::Ports::SerialPort(this->components));
                        this->btnConnect = (gcnew System::Windows::Forms::Button());
                        this->btnSendData = (gcnew System::Windows::Forms::Button());
                        this->txtSendData = (gcnew System::Windows::Forms::TextBox());
                        this->timer1 = (gcnew System::Windows::Forms::Timer(this->components));
                        this->lstCOMPorts = (gcnew System::Windows::Forms::ComboBox());
                        this->btnClose = (gcnew System::Windows::Forms::Button());
                        this->SuspendLayout();
                        // 
                        // txtDataReceived
                        // 
                        this->txtDataReceived->Location = System::Drawing::Point(12, 61);
                        this->txtDataReceived->Multiline = true;
                        this->txtDataReceived->Name = L"txtDataReceived";
                        this->txtDataReceived->Size = System::Drawing::Size(522, 143);
                        this->txtDataReceived->TabIndex = 0;
                        // 
                        // serialPort1
                        // 
                        this->serialPort1->DataReceived += gcnew System::IO::Ports::SerialDataReceivedEventHandler(this, &Form1::serialPort1_DataReceived);
                        // 
                        // btnConnect
                        // 
                        this->btnConnect->Location = System::Drawing::Point(12, 11);
                        this->btnConnect->Name = L"btnConnect";
                        this->btnConnect->Size = System::Drawing::Size(76, 19);
                        this->btnConnect->TabIndex = 1;
                        this->btnConnect->Text = L"Connect";
                        this->btnConnect->UseVisualStyleBackColor = true;
                        this->btnConnect->Click += gcnew System::EventHandler(this, &Form1::btnConnect_Click);
                        // 
                        // btnSendData
                        // 
                        this->btnSendData->Location = System::Drawing::Point(12, 36);
                        this->btnSendData->Name = L"btnSendData";
                        this->btnSendData->Size = System::Drawing::Size(76, 19);
                        this->btnSendData->TabIndex = 2;
                        this->btnSendData->Text = L"Send Data";
                        this->btnSendData->UseVisualStyleBackColor = true;
                        this->btnSendData->Click += gcnew System::EventHandler(this, &Form1::btnSendData_Click);
                        // 
                        // txtSendData
                        // 
                        this->txtSendData->Location = System::Drawing::Point(94, 37);
                        this->txtSendData->Name = L"txtSendData";
                        this->txtSendData->Size = System::Drawing::Size(440, 19);
                        this->txtSendData->TabIndex = 3;
                        // 
                        // timer1
                        // 
                        this->timer1->Enabled = true;
                        this->timer1->Interval = 1;
                        this->timer1->Tick += gcnew System::EventHandler(this, &Form1::timer1_Tick);
                        // 
                        // lstCOMPorts
                        // 
                        this->lstCOMPorts->DropDownStyle = System::Windows::Forms::ComboBoxStyle::DropDownList;
                        this->lstCOMPorts->FormattingEnabled = true;
                        this->lstCOMPorts->Location = System::Drawing::Point(94, 11);
                        this->lstCOMPorts->Name = L"lstCOMPorts";
                        this->lstCOMPorts->Size = System::Drawing::Size(99, 20);
                        this->lstCOMPorts->TabIndex = 4;
                        // 
                        // btnClose
                        // 
                        this->btnClose->Enabled = false;
                        this->btnClose->Location = System::Drawing::Point(199, 11);
                        this->btnClose->Name = L"btnClose";
                        this->btnClose->Size = System::Drawing::Size(78, 20);
                        this->btnClose->TabIndex = 5;
                        this->btnClose->Text = L"Close";
                        this->btnClose->UseVisualStyleBackColor = true;
                        this->btnClose->Click += gcnew System::EventHandler(this, &Form1::btnClose_Click);
                        // 
                        // Form1
                        // 
                        this->AutoScaleDimensions = System::Drawing::SizeF(6, 12);
                        this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
                        this->ClientSize = System::Drawing::Size(440, 218);
                        this->Controls->Add(this->btnClose);
                        this->Controls->Add(this->lstCOMPorts);
                        this->Controls->Add(this->txtSendData);
                        this->Controls->Add(this->btnSendData);
                        this->Controls->Add(this->btnConnect);
                        this->Controls->Add(this->txtDataReceived);
                        this->Name = L"Form1";
                        this->Text = L"VC++ COM port example";
                        this->ResumeLayout(false);
                        this->PerformLayout();

                }
#pragma endregion

        
                private: void UpdateCOMPortList(void)
                {
                        int i;
                        bool foundDifference;
                        cli::array<System::String^,1>^ portNames; 

                        i = 0;
                        foundDifference = false;
                        //もしCOMポート番号が変更になっていたら、COMポート番号が変更になった
                        //ということである。COMポートをはじめから精査する必要はない

                        portNames = serialPort1->GetPortNames();
                        if(lstCOMPorts->Items->Count == portNames->Length)
                        {
                                //全COMポートのチェック、すでにリストにあったポートがチェックする。
                                for(i=0;i<portNames->Length;i++)
                                {
                                        if(lstCOMPorts->Items[i]->Equals(portNames[i]) ==  false)
                                        {
                                                foundDifference = true;
                                        }
                                }
                        }
                        else
                        {
                                foundDifference = true;
                        }

                        
                        if (foundDifference == false)   //もし、何も変わっていないなら
                        {
                                return;
                        }

                        lstCOMPorts->Items->Clear();    //何か変わっている場合はリストクリア

                        for(i=0;i<portNames->Length;i++)        //全COMポートをリストに追加
                        {
                                lstCOMPorts->Items->Add(portNames[i]);
                        }

                        lstCOMPorts->SelectedIndex = 0;
                }

        
                private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) 
                {
                        //1msec毎にCOMポートリストチェック
                        UpdateCOMPortList();
                }


                private: System::Void btnConnect_Click(System::Object^  sender, System::EventArgs^  e) //Connectボタン
                {
                        try
                        {
                                //COMポート名取得
                                serialPort1->PortName = lstCOMPorts->Items[lstCOMPorts->SelectedIndex]->ToString();

                                //Open the COM port.
                                serialPort1->Open();    //COMポートオープン

                                //Change the state of the application objects
                                btnConnect->Enabled = false;
                                lstCOMPorts->Enabled = false;
                                btnClose->Enabled = true;

                                //Clear the textbox and print that we are connected.
                                txtDataReceived->Clear();
                                txtDataReceived->AppendText("Connected.\r\n");
                        }
                        catch(...)
                        {
                                //If there was an exception, then close the handle to 
                                //  the device and assume that the device was removed
                                btnClose_Click(this, gcnew EventArgs());
                        }
                }

                private: System::Void btnClose_Click(System::Object^  sender, System::EventArgs^  e) //クローズボタン
                {
                        //Reset the state of the application objects
                        btnClose->Enabled = false;
                        btnConnect->Enabled = true;
                        lstCOMPorts->Enabled = true;

                        try
                        {
                                serialPort1->DiscardInBuffer(); //バッファーデータ破棄
                                serialPort1->DiscardOutBuffer();//バッファーデータ破棄

                                serialPort1->Close();   //COMポートクローズ
                        }
                        //If there was an exeception then there isn't much we can
                        //  do.  The port is no longer available.
                        catch(...){}
                }

                //受信関数
                private: System::Void serialPort1_DataReceived(System::Object^  sender, System::IO::Ports::SerialDataReceivedEventArgs^  e) 
                {
                        
                        //この関数にはすべてのCOMポートからのデータが入ってくる。
                        //この関数はメインスレッドとは別のスレッド上にある。従って
                        //メインスレッドへのアクセスはデリゲート関数が必要となる。

                        //The ReadExisting() function will read all of the data that
                        //  is currently available in the COM port buffer.  In this 
                        //  example we are sending all of the available COM port data
                        //  to the SetText() function.
                        //
                        //  NOTE: the <SerialPort>_DataReceived() function is launched
                        //  in a seperate thread from the rest of the application.  A
                        //  delegate function is required in order to properly access
                        //  any managed objects inside of the other thread.  Since we
                        //  will be writing to a textBox (a managed object) the delegate
                        //  function is required.  Please see the SetText() function for 
                        //  more information about delegate functions and how to use them.
                        try
                        {
                                SetText(serialPort1->ReadExisting());
                        }
                        catch(...)
                        {
                                //If there was an exception, then close the handle to 
                                //  the device and assume that the device was removed
                                btnClose_Click(this, gcnew EventArgs());
                        }
                }

        
                private: void SetText(String^ text)
                {
                        //InvokeRequired required compares the thread ID of the
                        //  calling thread to the thread ID of the creating thread.
                        //  If these threads are different, it returns true.  We can
                        //  use this attribute to determine if we can append text
                        //  directly to the textbox or if we must launch an a delegate
                        //  function instance to write to the textbox.
                        if (this->txtDataReceived->InvokeRequired)      //invoke メソッドを呼び出す必要があるなら
                                                                                                                //別スレッドからの呼び出しであるなら
                        {
                                //InvokeRequired returned TRUE meaning that this function
                                //  was called from a thread different than the current
                                //  thread.  We must launch a deleage function.

                                //Create an instance of the SetTextCallback delegate and
                                //  assign the delegate function to be this function.  This
                                //  effectively causes this same SetText() function to be
                                //  called within the main thread instead of the second
                                //  thread.
                                SetTextCallback^ d = gcnew SetTextCallback(this,&VCCDC::Form1::SetText);
                                        //デリゲート関数へのインスタンスを生成し、SetText()関数へ割り当てる。

                                //Invoke the new delegate sending the same text to the
                                //  delegate that was passed into this function from the
                                //  other thread.
                                this->Invoke(d,gcnew String(text));     //デリゲートの非同期実行機能により、メインスレッドの
                                                                                                        //txtDataReceivedテキストボックスへ、受信時刻とは時間差をもって書き込む
                        }
                        else
                        {
                                //If this function was called from the same thread that 
                                //  holds the required objects then just add the text.
                                txtDataReceived->AppendText(text);
                        }
                }

                
                private: System::Void btnSendData_Click(System::Object^  sender, System::EventArgs^  e)//送信ボタン
                {
                        try
                        {
                                //仮想COMポートからの送信
                                serialPort1->Write(txtSendData->Text);
                        }
                        catch(...)
                        {
                                //If there was an exception, then close the handle to 
                                //  the device and assume that the device was removed
                                btnClose_Click(this, gcnew EventArgs());
                        }
                }
        };
}

PIC18F4550−PC 間  CDCクラス通信
 ( 自作PICキバン + Microchip社デモソフト)
<動作結果>
    デモソフト:
        Basic Demo

@コンボボックスからCOM8ポートを選択

AConnectボタンをクリック
  
→リッチテキストボックスにConnectedが表示される

BPICキバン上のSWを押すと文字列
 Button Pressed--がUSB送信され、左記ウィンドウの リッチテキストボックスに表示される。

Cテキストボックスに5をキーインて、Send Dataボタンを クリックする。
  →PICキバン側で+1インクリメントされた6がUSB通信で返信され、これが左記リッチテキストボックスに表示される

      ★全体のハード 及びPIC側のソフト
                 → 全体&PIC側参照

    デモソフト:
        Serial Emulator


送信側(USB通信):PICDEMサンプルソフト
受信側(RS232C通信):Tera Term

     

@左記ウィンドウのテキストボックスに文字列
 Hellow World !!をキーインして、Send Dataボタンをクリックする

APIC側キバン(PICDEM相当自作キバン)では、USBケーブル経由送信されてきたたこの文字列を受信した後、この文字列をRS232Cケーブル経由でRS232C通信でエコーバックする

BPC側ではエコーバックされてきたデータを通信ソフトTera Termで受信し、これをウィンドウに表示する。
                      


    デモソフト:
        Serial Emulator


送信側(RS232C通信):Tera Term
受信側(USB通信):PICDEMサンプルソフト


@Tera Term(注)に Data from TeraTerm とキーイン後、エンターキーを押しRS232C経由でPIC側(PICDEM相当)にデータを送信する。
APIC側では受信データをそのままUSBケーブル経由でPC側に送信する。
BPC側ではPICDEMデモソフトの右記ウィンドウのリッチテキストボックスで受信データを表示する。


      ★全体のハード 及びPIC側のソフト
                 → 全体&PIC側参照

(注)TeraTermのローカルエコーがONであるのでキーインしたデータが表示されている。




■ USB通信  PIC18 CDCクラス 文字列の送受信 (液晶付き) 
  マイクロチップ社のMCHPFSUSB Framework 2.3をダウンロードすると入手できる公開ソフトをベースに設計した、PCとPIC18F4550間のUSB通信(CDCクラス 液晶付)の例を紹介します。 ハード 及びPIC側のソフトは こちらを参照願います 
            

<試作品仕様>
 ・PC側からデータをPIC側にUSB CDCクラス通信で送信する。
 ・PIC側では受信した文字列を液晶上段に、受信データに基づき返信したデータを液晶下段に表示する。
 ・PC側でも受信したデータをテキストボックスに表示する。
 ・PC側からの送信データ 及びPIC側からの返信データは以下とする。
     @ How are you ?     →  I am fine !!
     A Your name ?      →  My name is PIC
     B This is a pen      →  Pardon?    

<プログラム>
Form1.h抜粋

          

#pragma once


namespace VCUSB_CDC4550LCD {

        using namespace System;
        using namespace System::ComponentModel;
        using namespace System::Collections;
        using namespace System::Windows::Forms;
        using namespace System::Data;
        using namespace System::Drawing;
    using namespace System::Threading;  //要追加


        public ref class Form1 : public System::Windows::Forms::Form
        {

                 static int numPorts = 0;

                  //文字を変換するクラスを宣言
      


        //受信スレッドから文字を取り込みtxtDataReceived textboxにデータを
        //書き込むデリゲート関数を宣言する。

        delegate void SetTextCallback(String^ text);

        public:
                Form1(void)
                {
                        InitializeComponent();

                         UpdateCOMPortList();    //使用可能COMポートリスト更新
                         SendData_ComboBox->Items->Clear();
             SendData_ComboBox->Items->Add("How are you ?   ");
             SendData_ComboBox->Items->Add("Your name ?     ");
             SendData_ComboBox->Items->Add("This is a pen   ");
             SendData_ComboBox->SelectedIndex = 0;        //コンボボックス選択値の初期値設定 



                }

         private: 
                void UpdateCOMPortList(void)
        {
                        int i;
            bool foundDifference;
            cli::array<System::String^,1>^ portNames; 

            i = 0;
            foundDifference = false;
            //もしCOMポート番号が変更になっていたら、COMポート番号が変更になった
            //ということである。COMポートをはじめから精査する必要はない

            portNames = serialPort1->GetPortNames();
            if(lstCOMPorts->Items->Count == portNames->Length)
            {
            //全COMポートのチェック、すでにリストにあったポートがチェックする。
                 for(i=0;i<portNames->Length;i++)
                 {
                     if(lstCOMPorts->Items[i]->Equals(portNames[i]) ==  false)
                     {
                         foundDifference = true;
                     }
                 }
             }
             else
             {
                 foundDifference = true;
             }
                        
             if (foundDifference == false)   //もし、何も変わっていないなら
             {
                 return;
             }

             lstCOMPorts->Items->Clear();    //何か変わっている場合はリストクリア

             for(i=0;i<portNames->Length;i++)        //全COMポートをリストに追加
             {
                 lstCOMPorts->Items->Add(portNames[i]);
                         }

             lstCOMPorts->SelectedIndex = 0;
         }

        private: System::
                Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) 
        {
            //1msec毎にCOMポートリストチェック
            UpdateCOMPortList();
        }

        private: System::
         Void btnConnect_Click(System::Object^  sender, System::EventArgs^  e)   //接続ボタン
         {
            try
            {
                 //COMポート名取得
                  serialPort1->PortName = lstCOMPorts->Items[lstCOMPorts->SelectedIndex]->ToString();

                //Open the COM port.
                serialPort1->Open();    //COMポートオープン

                //Change the state of the application objects
                btnConnect->Enabled = false;
                lstCOMPorts->Enabled = false;
                btnClose->Enabled = true;
                                
                //Clear the textbox and print that we are connected.
                txtDataReceived->Clear();
                txtDataReceived->AppendText("Connected.\r\n");
            }
            catch(...)
            {
                //If there was an exception, then close the handle to 
                //  the device and assume that the device was removed
                btnClose_Click(this, gcnew EventArgs());
            }
                }


        private: System::
                Void btnClose_Click(System::Object^  sender, System::EventArgs^  e) //クローズ
                {
                        //Reset the state of the application objects
            btnClose->Enabled = false;
            btnConnect->Enabled = true;
            lstCOMPorts->Enabled = true;

            try
            {
                                serialPort1->DiscardInBuffer(); //バッファーデータ破棄
                serialPort1->DiscardOutBuffer();//バッファーデータ破棄

                serialPort1->Close();   //COMポートクローズ
            }
                //If there was an exeception then there isn't much we can
                //  do.  The port is no longer available.
            catch(...){}
                }


  //受信関数

        private: System::
            Void serialPort1_DataReceived_1(System::Object^  sender, System::IO::Ports::SerialDataReceivedEventArgs^  e)
            {
            //この関数にはすべてのCOMポートからのデータが入ってくる。
            //この関数はメインスレッドとは別のスレッド上にある。従って
            //メインスレッドへのアクセスはデリゲート関数が必要となる。

            //The ReadExisting() function will read all of the data that
            //  is currently available in the COM port buffer.  In this 
            //  example we are sending all of the available COM port data
            //  to the SetText() function.
            //
            //  NOTE: the <SerialPort>_DataReceived() function is launched
            //  in a seperate thread from the rest of the application.  A
            //  delegate function is required in order to properly access
            //  any managed objects inside of the other thread.  Since we
            //  will be writing to a textBox (a managed object) the delegate
            //  function is required.  Please see the SetText() function for 
            //  more information about delegate functions and how to use them.

            try
            {
                                SetText(serialPort1->ReadExisting());

            }
            catch(...)
            {
                                //If there was an exception, then close the handle to 
                                //  the device and assume that the device was removed
                 btnClose_Click(this, gcnew EventArgs());
            }
                 }      

        private:
        void SetText(String^ text)
        {
                        //InvokeRequired required compares the thread ID of the
            //  calling thread to the thread ID of the creating thread.
            //  If these threads are different, it returns true.  We can
            //  use this attribute to determine if we can append text
            //  directly to the textbox or if we must launch an a delegate
            //  function instance to write to the textbox.
            if (this->txtDataReceived->InvokeRequired)      //invoke メソッドを呼び出す必要があるなら
                                                            //別スレッドからの呼び出しであるなら
            {
                //InvokeRequired returned TRUE meaning that this function
                //  was called from a thread different than the current
                //  thread.  We must launch a deleage function.
                //Create an instance of the SetTextCallback delegate and
                //  assign the delegate function to be this function.  This
                //  effectively causes this same SetText() function to be
                //  called within the main thread instead of the second
                //  thread.
                SetTextCallback^ d = gcnew SetTextCallback(this,&VCUSB_CDC4550LCD::Form1::SetText);
                     //デリゲート関数へのインスタンスを生成し、SetText()関数へ割り当てる。

                     //Invoke the new delegate sending the same text to the
                     //  delegate that was passed into this function from the
                     //  other thread.
                 this->Invoke(d,gcnew String(text));     //デリゲートの非同期実行機能により、メインスレッドの
                                                         //txtDataReceivedテキストボックスへ、受信時刻とは時間差をもって書き込む
               }
               else
               {
                    //If this function was called from the same thread that 
                    //  holds the required objects then just add the text.
                    txtDataReceived->AppendText(text);

               }
           }

        private: System::
         Void btnSendData_Click(System::Object^  sender, System::EventArgs^  e) 
         {
            try
            {
                                //仮想COMポートからの送信
                                String^ strSend = SendData_ComboBox->Text;
                                if (!strSend->EndsWith("\r")) strSend += "\r";  //改行が無かったら \rを追加する
                        serialPort1->Write(strSend);
            }
            catch(...)
            {
                //If there was an exception, then close the handle to 
                //  the device and assume that the device was removed
                btnClose_Click(this, gcnew EventArgs());
            }
          }


                ~Form1()
                {
                        if (components)
                        {
                                delete components;
                        }
                }
private: System::Windows::Forms::Button^  btnSendData;
private: System::Windows::Forms::ComboBox^  SendData_ComboBox;


        protected: 

private: System::Windows::Forms::TextBox^  txtDataReceived;

private: System::Windows::Forms::ComboBox^  lstCOMPorts;

private: System::Windows::Forms::Button^  btnConnect;

private: System::Windows::Forms::Button^  btnClose;

        private: System::Windows::Forms::Label^  label1;
        private: System::Windows::Forms::Label^  label2;
        private: System::Windows::Forms::Label^  label3;
        private: System::IO::Ports::SerialPort^  serialPort1;
        private: System::Windows::Forms::Timer^  timer1;
        private: System::ComponentModel::IContainer^  components;

        private:
                /// <summary>
                /// 必要なデザイナ変数です。
                /// </summary>


#pragma region Windows Form Designer generated code
                /// <summary>
                /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
                /// コード エディタで変更しないでください。
                /// </summary>
                void InitializeComponent(void)
                {
                        this->components = (gcnew System::ComponentModel::Container());
                        this->btnSendData = (gcnew System::Windows::Forms::Button());
                        this->SendData_ComboBox = (gcnew System::Windows::Forms::ComboBox());
                        this->txtDataReceived = (gcnew System::Windows::Forms::TextBox());
                        this->lstCOMPorts = (gcnew System::Windows::Forms::ComboBox());
                        this->btnConnect = (gcnew System::Windows::Forms::Button());
                        this->btnClose = (gcnew System::Windows::Forms::Button());
                        this->label1 = (gcnew System::Windows::Forms::Label());
                        this->label2 = (gcnew System::Windows::Forms::Label());
                        this->label3 = (gcnew System::Windows::Forms::Label());
                        this->serialPort1 = (gcnew System::IO::Ports::SerialPort(this->components));
                        this->timer1 = (gcnew System::Windows::Forms::Timer(this->components));
                        this->SuspendLayout();
                        // 
                        // btnSendData
                        // 
                        this->btnSendData->Location = System::Drawing::Point(209, 105);
                        this->btnSendData->Name = L"btnSendData";
                        this->btnSendData->Size = System::Drawing::Size(75, 23);
                        this->btnSendData->TabIndex = 0;
                        this->btnSendData->Text = L"送信";
                        this->btnSendData->UseVisualStyleBackColor = true;
                        this->btnSendData->Click += gcnew System::EventHandler(this, &Form1::btnSendData_Click);
                        // 
                        // SendData_ComboBox
                        // 
                        this->SendData_ComboBox->FormattingEnabled = true;
                        this->SendData_ComboBox->Location = System::Drawing::Point(55, 105);
                        this->SendData_ComboBox->Name = L"SendData_ComboBox";
                        this->SendData_ComboBox->Size = System::Drawing::Size(121, 20);
                        this->SendData_ComboBox->TabIndex = 1;
                        // 
                        // txtDataReceived
                        // 
                        this->txtDataReceived->Location = System::Drawing::Point(55, 188);
                        this->txtDataReceived->Multiline = true;
                        this->txtDataReceived->Name = L"txtDataReceived";
                        this->txtDataReceived->Size = System::Drawing::Size(121, 124);
                        this->txtDataReceived->TabIndex = 2;
                        // 
                        // lstCOMPorts
                        // 
                        this->lstCOMPorts->FormattingEnabled = true;
                        this->lstCOMPorts->Location = System::Drawing::Point(55, 33);
                        this->lstCOMPorts->Name = L"lstCOMPorts";
                        this->lstCOMPorts->Size = System::Drawing::Size(121, 20);
                        this->lstCOMPorts->TabIndex = 3;
                        // 
                        // btnConnect
                        // 
                        this->btnConnect->Location = System::Drawing::Point(209, 33);
                        this->btnConnect->Name = L"btnConnect";
                        this->btnConnect->Size = System::Drawing::Size(75, 23);
                        this->btnConnect->TabIndex = 4;
                        this->btnConnect->Text = L"接続";
                        this->btnConnect->UseVisualStyleBackColor = true;
                        this->btnConnect->Click += gcnew System::EventHandler(this, &Form1::btnConnect_Click);
                        // 
                        // btnClose
                        // 
                        this->btnClose->Location = System::Drawing::Point(311, 33);
                        this->btnClose->Name = L"btnClose";
                        this->btnClose->Size = System::Drawing::Size(75, 23);
                        this->btnClose->TabIndex = 5;
                        this->btnClose->Text = L"切断";
                        this->btnClose->UseVisualStyleBackColor = true;
                        this->btnClose->Click += gcnew System::EventHandler(this, &Form1::btnClose_Click);
                        // 
                        // label1
                        // 
                        this->label1->AutoSize = true;
                        this->label1->Location = System::Drawing::Point(73, 18);
                        this->label1->Name = L"label1";
                        this->label1->Size = System::Drawing::Size(82, 12);
                        this->label1->TabIndex = 6;
                        this->label1->Text = L"仮想COMポート";
                        // 
                        // label2
                        // 
                        this->label2->AutoSize = true;
                        this->label2->Location = System::Drawing::Point(82, 90);
                        this->label2->Name = L"label2";
                        this->label2->Size = System::Drawing::Size(57, 12);
                        this->label2->TabIndex = 7;
                        this->label2->Text = L"送信データ";
                        // 
                        // label3
                        // 
                        this->label3->AutoSize = true;
                        this->label3->Location = System::Drawing::Point(82, 173);
                        this->label3->Name = L"label3";
                        this->label3->Size = System::Drawing::Size(57, 12);
                        this->label3->TabIndex = 8;
                        this->label3->Text = L"受信データ";
                        // 
                        // serialPort1
                        // 
                        this->serialPort1->ReadTimeout = 500;
                        this->serialPort1->WriteTimeout = 500;
                        this->serialPort1->DataReceived += gcnew System::IO::Ports::SerialDataReceivedEventHandler(this, &Form1::serialPort1_DataReceived_1);
                        // 
                        // timer1
                        // 
                        this->timer1->Enabled = true;
                        this->timer1->Interval = 1;
                        // 
                        // Form1
                        // 
                        this->AutoScaleDimensions = System::Drawing::SizeF(6, 12);
                        this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
                        this->ClientSize = System::Drawing::Size(398, 342);
                        this->Controls->Add(this->label3);
                        this->Controls->Add(this->label2);
                        this->Controls->Add(this->label1);
                        this->Controls->Add(this->btnClose);
                        this->Controls->Add(this->btnConnect);
                        this->Controls->Add(this->lstCOMPorts);
                        this->Controls->Add(this->txtDataReceived);
                        this->Controls->Add(this->SendData_ComboBox);
                        this->Controls->Add(this->btnSendData);
                        this->Name = L"Form1";
                        this->Text = L"Form1";
                        this->ResumeLayout(false);
                        this->PerformLayout();

                }
#pragma endregion

};
}

<動作結果>
 @ PC側のウィンドウから仮想COM8ポートを選択してを接続ボタンをクリックする。
 A USB接続が完了して受信データテキストボックスに Connected が表示される。
 B 送信データ用コンボボックススから How are you ? を選択して送信ボタンをクリックする。
 C PIC側で受信した受信データ How are you ? が液晶の上段に表示されています。
 D 返信データとして I am fine !! がプログラムで選択され、これを液晶下段に表示されています。また、同時にPC側にも送信されています。
 E この結果、PC側では 受信したデータ I am fine !! を受信データ用テキストボックスに表示しています。
 F 同様に Your name ?  を送信した場合 My name is PIC が返信された場合も表示されています。
 G 同様に This is a pen  を送信した場合 Pardon? が返信された場合も表示されています。
 

PC側 
送受信ウィンドウ
PIC側 液晶画面



■ PIC18 汎用クラス Microchip社デモソフト(WDMドライバ編) 

  PICとUSB通信を行なう場合、マイクロチップ社から提供(公開)されているいるUSBフレームワークの汎用クラスを使うPC側のソフトは
 サンプルソフトがマイクロチップ社のHPに公開されています。以下のプログラムは  マイクロチップ社のMCHPFSUSB Framework 2.3
 ダウンロードして解凍後PCにできる …\Microchip Solutions\USB Device - MCHPUSB - Generic Driver Demo\PC Software\
 Visual C++ 2005 Express\Codeフォルダに収納されているVC++Cのサンプルプログラムです。
 このソフトは マイクロチップ  DemoBoard(PICDEM FS USB DM163025)($59.99ドル)用デモソフトのPC側ソフトです。 コメント行は
 翻訳、削除、追記等をしていますが 、ソースコードの変更は行なっていません。 全体のハード 及びPIC側のソフトは 全体&PIC側
 参照願います。 ドライバーをインストール後 API関数により以下のような通信が可能となります。

<試作品の仕様>
  ・PIC側の可変抵抗器の値をPC側に側に ポーリングによるUSB 汎用(Generic)クラス通信で送信する。
  ・デバイスドライバはWDM(Windows Driver Model)ドライバを用いる
  ・PIC側からの送信データは8msec毎にPC側におくり、PC側では送られてきたデータをステータスバーに表示する。
  ・送受信中USBコネクタを抜き差ししてもPnP(Plug and Play)によりアタッチの自動検出を支障なく実施してステータスバー表示がすみやかに
   できること。

<プログラム>
<Form1.h>抜粋

#pragma once //Includes #include <windows.h> //Definitions for various common and not so common types like DWORD, PCHAR, HANDLE, etc. #include <Dbt.h> //Need this for definitions of WM_DEVICECHANGE messages #include "mpusbapi.h" //このヘッダーファイルをプロジェクトファイルと同じフォルダに置く必要あり #define DeviceVID_PID "vid_04d8&pid_000c" // ★ USBのベンダーIDとプロダクトIDの設定 //-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------- //-------------------------------------------------------------------------------------------------------------------- namespace MCHPUSBPnPDemo { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; //-------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK------------------------------------ using namespace System::Threading; using namespace System::Runtime::InteropServices; // "unmanaged" codeの場合必要 // マイクロチップのヘッダーファイルは末尾にUMがついている。ユーザのヘッダーファイル命名時に考慮すること #ifdef UNICODE //ユニコードとAnsiコードの混在対応 :必須 #define Seeifdef Unicode #else #define Seeifdef Ansi #endif //以下 mpusbapi.dllにより参照できる関数 (_mpusbspi.h 及び_mpusbapi.cpp参照) [DllImport("MPUSBAPI.dll" , EntryPoint="_MPUSBGetDLLVersion")] extern "C" DWORD MPUSBGetDLLVersion(void); //mpusbapi.dllのバージョン取得関数 [DllImport("MPUSBAPI.dll" , EntryPoint="_MPUSBGetDeviceCount")] extern "C" DWORD MPUSBGetDeviceCount(PCHAR pVID_PID); //VIDとPIDのチェック関数 //戻り値:ベンダーID 及びプロダクトIDが一致した数 //    一致したものがない場合は0 [DllImport("MPUSBAPI.dll" , EntryPoint="_MPUSBOpen")] extern "C" HANDLE MPUSBOpen(DWORD instance, // USBエンドポイントオープン関数 戻り値:エンドポイントのハンドル PCHAR pVID_PID, // VIDとPIDのポインタt PCHAR pEP, // エンドポイントのポインタ DWORD dwDir, // エンドポイントの入出力指定 入力:MP_READ 出力:MP_WRITE DWORD dwReserved);// (予約) [DllImport("MPUSBAPI.dll" , EntryPoint="_MPUSBClose")] //USBエンドポイントクローズ関数 extern "C" BOOL MPUSBClose(HANDLE handle); //ハンドル [DllImport("MPUSBAPI.dll" , EntryPoint="_MPUSBRead")] // USBデータ読み出し関数 extern "C" DWORD MPUSBRead(HANDLE handle, // ハンドル PVOID pData, // データバッファーのポインタ DWORD dwLen, // 取得予定データ長(バイト) PDWORD pLength, // 取得したデータ長(バイト) DWORD dwMilliseconds);// タイムアウト時間(msec) [DllImport("MPUSBAPI.dll" , EntryPoint="_MPUSBWrite")] // USBデータ書き込み関数 extern "C" DWORD MPUSBWrite(HANDLE handle, // ハンドル PVOID pData, // 送信データのポインタ DWORD dwLen, // 送信予定データ長(バイト) PDWORD pLength, // 送信したデータ長(バイト) DWORD dwMilliseconds);// タイムアウト時間(msec) [DllImport("MPUSBAPI.dll" , EntryPoint="_MPUSBReadInt")] //USBインタラプト転送データ読み出し関数 extern "C" DWORD MPUSBReadInt(HANDLE handle, // ハンドルt PVOID pData, // データバッファーのポインタ DWORD dwLen, // 取得予定データ長(バイト) PDWORD pLength, // 取得したデータ長(バイト) DWORD dwMilliseconds);// タイムアウト時間(msec) //全 _DEVICECHANGEメッセージを受信する場合は以下の関数が必要となる。詳細MSDN参照、 //命名はRegisterDeviceNotificationUMに変更されている [DllImport("user32.dll" , CharSet = CharSet::Seeifdef, EntryPoint="RegisterDeviceNotification")] extern "C" HDEVNOTIFY WINAPI RegisterDeviceNotificationUM( HANDLE hRecipient, LPVOID NotificationFilter, DWORD Flags); //----------------Global variables used in this application-------------------------------- HANDLE EP1INHandle = INVALID_HANDLE_VALUE; //エンドポイント1のデータ入力用ハンドル HANDLE EP1OUTHandle = INVALID_HANDLE_VALUE; //エンドポイント1のデータ出力用ハンドル unsigned int ADCValue = 0; BOOL AttachedState = FALSE; //USB接続状態(通信路がプラグアンドプレイ接続後良好な通信路状態となっているか) //-------------------------------------------------------END CUT AND PASTE BLOCK---------------------------------- //--------------------------------------------------------------------------------------------------------------- public ref class Form1 : public System::Windows::Forms::Form { public: Form1(void) //コンストラクタ { InitializeComponent(); //----------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-------------------------------------- //Additional constructor code //デバイスのプラグイン/プラグアウトの検出 //デバイスのプラグイン/プラグアウトの検出には、ハードウェア構成に変化があった時に //Windowsから通知されるWM_DEVICECHANGEメッセージを使用します。 //ウィンドウ作成時にRegisterDeviceNotificationでドライバのGUIDを設定することにより、 //指定GUIDのドライバからのWM_DEVICECHANGEメッセージを受け取ることができます //Globally Unique Identifier (GUID). Windows uses GUIDs to identify things. GUID InterfaceClassGuid = {0xa5dcbf10, 0x6530, 0x11d2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}; //Globally Unique Identifier (GUID) for USB peripheral devices //Register for WM_DEVICECHANGE notifications: DEV_BROADCAST_DEVICEINTERFACE MyDeviceBroadcastHeader;// = new DEV_BROADCAST_HDR; MyDeviceBroadcastHeader.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; MyDeviceBroadcastHeader.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); MyDeviceBroadcastHeader.dbcc_reserved = 0; //Reserved says not to use... MyDeviceBroadcastHeader.dbcc_classguid = InterfaceClassGuid; RegisterDeviceNotificationUM((HANDLE)this->Handle, &MyDeviceBroadcastHeader, DEVICE_NOTIFY_WINDOW_HANDLE); //ウィンドウ生成時のベンダーID&プロダクトIDチェック if(MPUSBGetDeviceCount(DeviceVID_PID)) // VID、PIDが一致した場合 { EP1OUTHandle = MPUSBOpen(0, DeviceVID_PID, "\\MCHP_EP1", MP_WRITE, 0); //書き込み用エンドポイントオープン EP1INHandle = MPUSBOpen(0, DeviceVID_PID, "\\MCHP_EP1", MP_READ, 0); //読み込み用エンドポイントオープン AttachedState = TRUE; //USBアタッチOK StatusBox_txtbx->Text = "Device Found: AttachedState = TRUE"; //テキストボックスに"Device Found: AttachedState = TRUE"を表示 label2->Enabled = true; //ラベル2をenable } else //VID、PIDが一致しない場合 { StatusBox_txtbx->Text = "Device Not Detected: Verify Connection/Correct Firmware"; AttachedState = FALSE; label2->Enabled = false; //ラベル2をdisabble } ReadWriteThread->RunWorkerAsync(); //Read/Write operations は別スレッドのこと //別スレッドにしないと関数やユーザインターフェースに支障がでることがある。 //-------------------------------------------------------END CUT AND PASTE BLOCK----------------------------------------------- //----------------------------------------------------------------------------------------------------------------------------- } protected: ~Form1() //デストラクタ { //------------------------------------------------------------------------------------------------------------------------------ //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK--------------------------------------------- //Make sure to close any open handles, before exiting the application if (EP1OUTHandle != INVALID_HANDLE_VALUE) MPUSBClose (EP1OUTHandle); //書き込み用のエンドポイントクローズ if (EP1INHandle != INVALID_HANDLE_VALUE) MPUSBClose (EP1INHandle); //読み込み用のエンドポイントクローズ //-------------------------------------------------------END CUT AND PASTE BLOCK------------------------------------------------- //------------------------------------------------------------------------------------------------------------------------------ if (components) { delete components; } } private: System::Windows::Forms::ProgressBar^ progressBar1; protected: private: System::ComponentModel::BackgroundWorker^ ReadWriteThread; private: System::Windows::Forms::Timer^ timer1; private: System::Windows::Forms::TextBox^ StatusBox_txtbx; private: System::Windows::Forms::Label^ label1; private: System::Windows::Forms::Label^ label2; private: System::ComponentModel::IContainer^ components; private: /// <summary> /// Required designer variable. /// </summary> #pragma region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> void InitializeComponent(void) { this->components = (gcnew System::ComponentModel::Container()); this->progressBar1 = (gcnew System::Windows::Forms::ProgressBar()); this->ReadWriteThread = (gcnew System::ComponentModel::BackgroundWorker()); this->timer1 = (gcnew System::Windows::Forms::Timer(this->components)); this->StatusBox_txtbx = (gcnew System::Windows::Forms::TextBox()); this->label1 = (gcnew System::Windows::Forms::Label()); this->label2 = (gcnew System::Windows::Forms::Label()); this->SuspendLayout(); // // progressBar1 // this->progressBar1->BackColor = System::Drawing::Color::White; this->progressBar1->ForeColor = System::Drawing::Color::Red; this->progressBar1->Location = System::Drawing::Point(13, 69); this->progressBar1->Maximum = 1024; this->progressBar1->Name = L"progressBar1"; this->progressBar1->Size = System::Drawing::Size(183, 21); this->progressBar1->Step = 1; this->progressBar1->Style = System::Windows::Forms::ProgressBarStyle::Continuous; this->progressBar1->TabIndex = 9; // // ReadWriteThread // this->ReadWriteThread->WorkerReportsProgress = true; this->ReadWriteThread->DoWork += gcnew System::ComponentModel::DoWorkEventHandler(this, &Form1::ReadWriteThread_DoWork); // // timer1 // this->timer1->Enabled = true; this->timer1->Interval = 8; this->timer1->Tick += gcnew System::EventHandler(this, &Form1::timer1_Tick); // // StatusBox_txtbx // this->StatusBox_txtbx->Location = System::Drawing::Point(13, 12); this->StatusBox_txtbx->Multiline = true; this->StatusBox_txtbx->Name = L"StatusBox_txtbx"; this->StatusBox_txtbx->Size = System::Drawing::Size(298, 19); this->StatusBox_txtbx->TabIndex = 11; // // label1 // this->label1->AutoSize = true; this->label1->Location = System::Drawing::Point(317, 15); this->label1->Name = L"label1"; this->label1->Size = System::Drawing::Size(62, 12); this->label1->TabIndex = 12; this->label1->Text = L"Status Box"; // // label2 // this->label2->AutoSize = true; this->label2->Enabled = false; this->label2->Location = System::Drawing::Point(127, 54); this->label2->Name = L"label2"; this->label2->Size = System::Drawing::Size(98, 12); this->label2->TabIndex = 13; this->label2->Text = L"ANx/POT Voltage"; // // Form1 // this->AutoScaleDimensions = System::Drawing::SizeF(6, 12); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(386, 109); this->Controls->Add(this->label2); this->Controls->Add(this->label1); this->Controls->Add(this->StatusBox_txtbx); this->Controls->Add(this->progressBar1); this->Name = L"Form1"; this->Text = L"MCHPUSB PnP Demo"; this->ResumeLayout(false); this->PerformLayout(); } #pragma endregion //--------------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK------------------------------------------- private: System::Void ReadWriteThread_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) { //このスレッドは実際にはUSBの読み書きは行なわない(但し、AttachedState == TRUE) //したがって、非同期でアプリケーションの値は書き換わる   unsigned char Buffer [64]; DWORD ActualLength; while(true) { Buffer[0] = 0x37; //READ_POT command (see the firmware source code), gets 10-bit ADC Value if(AttachedState == TRUE) //USBアタッチOKなら { if(MPUSBWrite(EP1OUTHandle, Buffer, 1, &ActualLength, 1000)) //エンドポイント1に1バイト書き込み、タイムアウト時間1000msec  { if(MPUSBRead(EP1INHandle, Buffer, 3, &ActualLength, 1000)) //エンドポイント1から3バイト読み込み、タイムアウト時間1000msec { ADCValue = (Buffer[2] << 8) + Buffer[1]; //8ビットビットシフト → データ16ビット化 } } } else { ADCValue = 0; } Sleep(4); //若干(4msec )の遅れが必要である。この遅れがないとCPUの利用効率の極端な増大を招く } } private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) { //8msec毎にインターバルタイマがONしたときグローバル変数を読み取りユーザインターフェースのウィンドウが更新される。 if(ADCValue > (unsigned int)progressBar1->Maximum) { ADCValue = progressBar1->Maximum; } progressBar1->Value = ADCValue; //プログレスバーの値更新 } protected: virtual void WndProc( Message% m ) override //コールバック関数(OSからの呼出時の処理関数) { if(m.Msg == WM_DEVICECHANGE) //ウィンドウメッセージがWM_DEVICECHANGE messagesの場合 { if(((int)m.WParam == DBT_DEVICEARRIVAL) || ((int)m.WParam == DBT_DEVICEREMOVEPENDING) || ((int)m.WParam == DBT_DEVICEREMOVECOMPLETE) || ((int)m.WParam == DBT_CONFIGCHANGED) ) { //WM_DEVICECHANGEには多々ある。必ずしも接続したPICからとは限らない。 if(MPUSBGetDeviceCount(DeviceVID_PID)) //VID、PIDが一致した場合 { if(AttachedState == FALSE) //USBアタッチ状態がNGの場合 { EP1OUTHandle = MPUSBOpen(0, DeviceVID_PID, "\\MCHP_EP1", MP_WRITE, 0); //書き込み用エンドポイント1オープン EP1INHandle = MPUSBOpen(0, DeviceVID_PID, "\\MCHP_EP1", MP_READ, 0); //読み込み用エンドポイント1オープン AttachedState = TRUE; //USBアタッチ状態OK StatusBox_txtbx->Text = "Device Found: AttachedState = TRUE"; label2->Enabled = true; //Make the label no longer greyed out } } else //VID、PIDが一致しない場合 { if(MPUSBClose(EP1OUTHandle)) //書き込み用エンドポイント1クローズ EP1OUTHandle = INVALID_HANDLE_VALUE; if(MPUSBClose(EP1INHandle)) //読み込み用エンドポイント1クローズ EP1INHandle = INVALID_HANDLE_VALUE; AttachedState = FALSE; StatusBox_txtbx->Text = "Device Not Detected: Verify Connection/Correct Firmware"; label2->Enabled = false; //Make the label greyed out } } } Form::WndProc( m ); } //end WinProc( ) //-------------------------------------------------------END CUT AND PASTE BLOCK---------------------------------- //---------------------------------------------------------------------------------------------------------------- }; //end public ref class Form1 : public System::Windows::Forms::Form } //end namespace MCHPUSBPnPDemo

 

<動作結果>

              PIC側ターゲットボードの可変抵抗器電圧値を8msec毎に
読み込みステータスバーに表示が行なわれている。



■ PIC18 汎用クラス Microchip社デモソフト(WinUSBドライバ編) 

  PICとUSB通信を行なう場合、マイクロチップ社から提供(公開)されているいるUSBフレームワークの汎用クラスを使うPC側のソフトは
 サンプルソフトがマイクロチップ社のHPに公開されています。以下のプログラムは  マイクロチップ社のMCHPFSUSB Framework 2.4
 ダウンロードして解凍後PCにできる …\Microchip Solutions\USB Device - WinUSB - Generic Driver Demo\WinUSB Simple Demo - PC Application - MS VC++ 2005 Express\Codeフォルダに収納されているVC++Cのサンプルプログラムです。
 このソフトは マイクロチップ  DemoBoard(PICDEM FS USB DM163025)($59.99ドル)用デモソフトのPC側ソフトです。 コメント行は
 翻訳、削除、追記等をしていますが 、ソースコードの変更は行なっていません。 全体のハード 及びPIC側のソフトは 全体&PIC側
 参照願います。 ドライバーをインストール後 API関数により以下のような通信が可能となります。

 <試作品の仕様>
  ・PC側とPIC側の通信として、割り込みによるUSB 汎用(Generic)クラス通信を用いる。
  ・デバイスドライバとしてはWinUSBを使用する
  ・PCのウィンドウ上のボタンによりPIC側基板上にあるLEDをON/OFFする。
  ・PIC側キバン上のスイッチをON/OFFした場合のをモニタPC側ウィンドウ上に表示する。
   

プログラム
<Form1.h>抜粋
#include <Windows.h>    //Definitions for various common and not so common types like DWORD, PCHAR, HANDLE, etc.
#include <setupapi.h>   //From Windows Server 2003 R2 Platform SDK.  Untested with Windows SDK.  Setupapi.h provides
                                                //definitions needed for the SetupDixxx() functions, which we use to find our plug and 
                                                //play device.  If getting build errors when trying to compile this project, make sure the
                                                //platform SDK is correctly installed, and that it has been integrated into the VC++ development
                                                //environment.  In other words, make sure the include paths have been setup correctly in this IDE.
                                                //Microsoft created some small tutorial videos showing how to do this.
                                                //These may be located at:
                                                //http://msdn.microsoft.com/en-us/visualc/aa336415.aspx 
                                                //The link is functional as of 21 May 2008, but could move.  If the above link does not work,
                                                //try searching for "Video1_PSDK.wmv", which may be associated with:
                                                //"Using Visual C++ Express Edition and the Platform SDK"
                                                //Also see the below comments on how to add directories to the include path.


                                                //IMPORTANT: READ THIS BEFORE TRYING TO BUILD THIS CODE
                                                //----------------------------------------------------
#include <Winusb.h>             //Winusb.h comes as a part of the Windows Driver Kit (WDK) build 6001.18001 (and presumably later versions).
                                                //The WDK is currently a free download from http://connect.microsoft.com.  Please follow these steps in order
                                                //  to find the WDK.
                                                //  1) go to http://connect.microsoft.com
                                                //  2) click the sign-in button.  If you already have a Windows Live ID then sign in with that.
                                                //              If not then create one now.
                                                //      3) Once signed in it should bring you to your dashboard.  Click on "CONNECTION DIRECTORY"
                                                //  4) find the WDK on the list.
                                                //  5) Click "Apply Now".  This should bring you to the WDK home page
                                                //  6) Click "Downloads" on the left menu bar
                                                //  7) Click on "WDK for Server 2008"
                                                //You will need the WDK build 6001.18001 (the latest currently available 19 May 2008) installed on
                                                //your computer to use this source code.  Once you have it installed, you will also need to add
                                                //the include path directories from the WDK to your VC++ IDE.  This can be done by clicking
                                                //Tools-->Options-->+Projects and Solutions-->VC++ Directories-->Show Directories for: "Include files"
                                                //Then click the Folder icon (new line) and then the "..." button and add these directories:
                                                //C:\WINDDK\6001.18001\inc\ddk
                                                //C:\WINDDK\6001.18001\inc\api
                                                //The above directory locations assume the default location for the WDK.
                                                //If the above procedure is not followed correctly, a variety of build errors looking for various
                                                //files such as winusbio.h, usb200.h, usb100.h, usb.h, etc. will occur.

//<参考>WDK関連の設定について
//★ WDKドライバーをつかってコンパイルするには以下のファイルをインクルードする必要がある。各ファイルはWDK(Windows Drivers Kit)の中にあり、
//マイクロソフトのサイトからダウンロードして PCにセットアップする必要がある。マイクロチップのUSB関連のファイルと全く同名のファイルも
//あるのでインクルードに際しては注意する必要がある。
// usb.h  usb100.h  usb200.h   winusb.h  etc.
//(注) WINDOWS.Hのあるc:\WinDDK\6001.18002\inc\apiフォルダをインクルードバスに設定すると大量コンパイルエラーがでたので、このフォルダへの
// パス設定はやめ、このフォルダ内にあったusb.h  usb100.h  usb200.h はc:\WinDDK\6001.18002\inc\ddkフォルダにコピーしてコンパイルを
//実施した。


                                
#define MY_DEVICE_ID  "Vid_04d8&Pid_0053"       //ベンダーIDとプロダクトIDの定義


namespace SimpleWinUSBDemo 
{

        using namespace System;
        using namespace System::ComponentModel;
        using namespace System::Collections;
        using namespace System::Windows::Forms;
        using namespace System::Data;
        using namespace System::Drawing;

        using namespace System::Runtime::InteropServices;       //アンマネージドコードを使用する場合は必要 
                                                                                                                //Need this to support "unmanaged" code.


        /*
        .NETのマネージド環境でも、アンマネージドの関数を使用するには.dllをインポートすればよい。
        In order to use these unmanaged functions from within the managed .NET environment, we need
        to explicitly import the functions which we will be using from other .DLL file(s).  Simply
        including the appropriate header files is not enough. 

        これらのアンマネージド関数には、最後に"UM" (unmanaged) をつけて命名した。これは、MSDNの中に
        ある我々がすでに使用している関数との混乱をさけるためである。
        Note: In order to avoid potential name conflicts in the header files (which we still use),
        I have renamed the functions by adding "UM" (unmanaged) onto the end of them.  To find 
        documentation for the functions in MSDN, search for the function name without the extra 
        "UM" attached.

        関数がユニコードでできているのか、Ansiコードでできているかコンパイラが判別して、
        条件付きコンパイルができるようにできるように下記プリプロセッサを記載しておく。
        Note2: In the header files (such as setupapi.h), normally the function names are 
        remapped, depending upon if UNICODE is defined or not.  For example, two versions of the
        function SetupDiGetDeviceInterfaceDetail() exist.  One for UNICODE, and one for ANSI.  
        If the wrong version of the function is called, things won't work correctly.  Therefore,
        in order to make sure the correct one gets called (based on your compiler settings, which
        may or may not define "UNICODE"), it is useful to explicity specify the CharSet when doing
        the DLL import.
        */

        #ifdef UNICODE
        #define Seeifdef        Unicode
        #else
        #define Seeifdef        Ansi    //特に指定がない場合はAnsiコードとみなす
        #endif

        //<以下 <setupapi.h、Winusb.h より参照できる関数>

        // 指定GUID(注)のUSBデバイスがあるか調べる関数
        // (1つのデバイスのハンドルが返される)
        //(注)GUID(Globally Unique Identifier:グーイッド):グローバル識別子(2の128乗個の乱数からなる)
        //Returns a HDEVINFO type for a device information set (WinUSB devices in
        //our case).  We will need the HDEVINFO as in input parameter for calling many of
        //the other SetupDixxx() functions.
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetClassDevs")]             
        extern "C" HDEVINFO  SetupDiGetClassDevsUM(
                LPGUID  ClassGuid,      //グローバル識別子 Input: Supply the class GUID here. 
                PCTSTR  Enumerator,     //NULL Input: Use NULL here, not important for our purposes
                HWND  hwndParent,       //NULL Input: Use NULL here, not important for our purposes
                DWORD  Flags);  //デバイス情報セットの構築に使われる制御オプションInput: Flags describing what kind of filtering to use.



        //USBデバイスのインタフェース情報を取得する関数
        //Gives us "PSP_DEVICE_INTERFACE_DATA" which contains the Interface specific GUID (different
        //from class GUID).  We need the interface GUID to get the device path.
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInterfaces")]                             
        extern "C" WINSETUPAPI BOOL WINAPI  SetupDiEnumDeviceInterfacesUM(
                HDEVINFO  DeviceInfoSet,                        //Input: Give it the HDEVINFO we got from SetupDiGetClassDevs()
                PSP_DEVINFO_DATA  DeviceInfoData,       //Input (optional)
                LPGUID  InterfaceClassGuid,                     //Input 
                DWORD  MemberIndex,                                     //Input: "Index" of the device you are interested in getting the path for.
                PSP_DEVICE_INTERFACE_DATA  DeviceInterfaceData);//Output: This function fills in an "SP_DEVICE_INTERFACE_DATA" structure.


        //USBデバイスのメモリを破棄し、デバイスの情報を破棄する関数
        //SetupDiDestroyDeviceInfoList() frees up memory by destroying a DeviceInfoList
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiDestroyDeviceInfoList")]
        extern "C" WINSETUPAPI BOOL WINAPI  SetupDiDestroyDeviceInfoListUM(                     
                HDEVINFO  DeviceInfoSet);                       //Input: Give it a handle to a device info list to deallocate from RAM.


        //USBデバイス情報取得が完了したかどうかを調べる関数
        //SetupDiEnumDeviceInfo() fills in an "SP_DEVINFO_DATA" structure, which we need for SetupDiGetDeviceRegistryProperty()
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInfo")]
        extern "C" WINSETUPAPI BOOL WINAPI  SetupDiEnumDeviceInfoUM(
                HDEVINFO  DeviceInfoSet,
                DWORD  MemberIndex,
                PSP_DEVINFO_DATA  DeviceInfoData);


        //USBデバイスのプロパティを確認する関数
        //SetupDiGetDeviceRegistryProperty() gives us the hardware ID, which we use to check to see if it has matching VID/PID
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceRegistryProperty")]
        extern "C"      WINSETUPAPI BOOL WINAPI  SetupDiGetDeviceRegistryPropertyUM(
                HDEVINFO  DeviceInfoSet,
                PSP_DEVINFO_DATA  DeviceInfoData,
                DWORD  Property,
                PDWORD  PropertyRegDataType,
                PBYTE  PropertyBuffer,   
                DWORD  PropertyBufferSize,  
                PDWORD  RequiredSize);


        //USBデバイスのインターフェース詳細について調べる関数
        //SetupDiGetDeviceInterfaceDetail() gives us a device path, which is needed before CreateFile() can be used.
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceInterfaceDetail")]
        extern "C" BOOL SetupDiGetDeviceInterfaceDetailUM(
                HDEVINFO DeviceInfoSet,                                                                         //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs()
                PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,                          //Input: Pointer to an structure which defines the device interface.  
                PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData,     //Output: Pointer to a strucutre, which will contain the device path.
                DWORD DeviceInterfaceDetailDataSize,                                            //Input: Number of bytes to retrieve.
                PDWORD RequiredSize,                                                                            //Output (optional): Te number of bytes needed to hold the entire struct 
                PSP_DEVINFO_DATA DeviceInfoData);                                                       //Output


        //USBデバイスの初期化をおこなう関数
        //WinUsb_Initialize() needs to be called before the application can begin sending/receiving data with the USB device.
        [DllImport("winusb.dll" , CharSet = CharSet::Seeifdef, EntryPoint="WinUsb_Initialize")]
        extern "C" BOOL WinUsb_Initialize(
                HANDLE  DeviceHandle,
                PWINUSB_INTERFACE_HANDLE InterfaceHandle);

        //USBデバイスとのパイプ(エンドポイント)に書き込む関数
        //WinUsb_WritePipe() is the basic function used to write data to the USB device (sends data to OUT endpoints on the device)
        [DllImport("winusb.dll" , CharSet = CharSet::Seeifdef, EntryPoint="WinUsb_WritePipe")]
        extern "C" BOOL WinUsb_WritePipe(
                WINUSB_INTERFACE_HANDLE InterfaceHandle,
                UCHAR PipeID,
                PUCHAR Buffer,
                ULONG BufferLength,
                PULONG LengthTransferred,
                LPOVERLAPPED Overlapped);

        //USBデバイスとのパイプ(エンドポイント)からデータを読み込む関数
        //WinUsb_ReadPipe() is the basic function used to read data from the USB device (polls for and obtains data from
        //IN endpoints on the device)
        [DllImport("winusb.dll" , CharSet = CharSet::Seeifdef, EntryPoint="WinUsb_ReadPipe")]
        extern "C" BOOL WinUsb_ReadPipe(
                WINUSB_INTERFACE_HANDLE InterfaceHandle,
                UCHAR PipeID,
                PUCHAR Buffer,
                ULONG BufferLength,
                PULONG LengthTransferred,
                LPOVERLAPPED Overlapped);


//グローバル変数
        HANDLE MyDeviceHandle = INVALID_HANDLE_VALUE;           //USBデバイス用のハンドル
        WINUSB_INTERFACE_HANDLE MyWinUSBInterfaceHandle;        //インターフェース用ハンドル
                                                                                                                //And then can call WinUsb_Initialize() to get the interface handle
                                                                                                                //which is needed for doing other operations with the device (like
                                                                                                                //reading and writing to the USB device).




        public ref class Form1 : public System::Windows::Forms::Form
        {
        public:
                Form1(void)
                {
                        InitializeComponent();
                }

        protected:
                ~Form1()
                {
                        if (components)
                        {
                                delete components;
                        }
                }


        private: System::Windows::Forms::Button^  Connect_btn;
        protected: 
        private: System::Windows::Forms::Button^  ToggleLED_btn;
        private: System::Windows::Forms::Button^  GetPushbuttonState_btn;
        private: System::Windows::Forms::Label^  StateLabel;

        private:
                /// <summary>
                /// Required designer variable.
                /// </summary>
                System::ComponentModel::Container ^components;

#pragma region Windows Form Designer generated code
                /// <summary>
                /// Required method for Designer support - do not modify
                /// the contents of this method with the code editor.
                /// </summary>
                void InitializeComponent(void)
                {
                        this->Connect_btn = (gcnew System::Windows::Forms::Button());
                        this->ToggleLED_btn = (gcnew System::Windows::Forms::Button());
                        this->GetPushbuttonState_btn = (gcnew System::Windows::Forms::Button());
                        this->StateLabel = (gcnew System::Windows::Forms::Label());
                        this->SuspendLayout();
                        // 
                        // Connect_btn
                        // 
                        this->Connect_btn->Location = System::Drawing::Point(12, 12);
                        this->Connect_btn->Name = L"Connect_btn";
                        this->Connect_btn->Size = System::Drawing::Size(75, 23);
                        this->Connect_btn->TabIndex = 1;
                        this->Connect_btn->Text = L"Connect";
                        this->Connect_btn->UseVisualStyleBackColor = true;
                        this->Connect_btn->Click += gcnew System::EventHandler(this, &Form1::Connect_btn_Click);
                        // 
                        // ToggleLED_btn
                        // 
                        this->ToggleLED_btn->Enabled = false;
                        this->ToggleLED_btn->Location = System::Drawing::Point(93, 12);
                        this->ToggleLED_btn->Name = L"ToggleLED_btn";
                        this->ToggleLED_btn->Size = System::Drawing::Size(125, 23);
                        this->ToggleLED_btn->TabIndex = 2;
                        this->ToggleLED_btn->Text = L"Toggle LED(s)";
                        this->ToggleLED_btn->UseVisualStyleBackColor = true;
                        this->ToggleLED_btn->Click += gcnew System::EventHandler(this, &Form1::ToggleLED_btn_Click);
                        // 
                        // GetPushbuttonState_btn
                        // 
                        this->GetPushbuttonState_btn->Enabled = false;
                        this->GetPushbuttonState_btn->Location = System::Drawing::Point(93, 41);
                        this->GetPushbuttonState_btn->Name = L"GetPushbuttonState_btn";
                        this->GetPushbuttonState_btn->Size = System::Drawing::Size(125, 23);
                        this->GetPushbuttonState_btn->TabIndex = 3;
                        this->GetPushbuttonState_btn->Text = L"Get Pushbutton State";
                        this->GetPushbuttonState_btn->UseVisualStyleBackColor = true;
                        this->GetPushbuttonState_btn->Click += gcnew System::EventHandler(this, &Form1::GetPushbuttonState_btn_Click);
                        // 
                        // StateLabel
                        // 
                        this->StateLabel->AutoSize = true;
                        this->StateLabel->Enabled = false;
                        this->StateLabel->Location = System::Drawing::Point(224, 51);
                        this->StateLabel->Name = L"StateLabel";
                        this->StateLabel->Size = System::Drawing::Size(84, 13);
                        this->StateLabel->TabIndex = 4;
                        this->StateLabel->Text = L"State: Unknown";
                        // 
                        // Form1
                        // 
                        this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
                        this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
                        this->ClientSize = System::Drawing::Size(333, 96);
                        this->Controls->Add(this->StateLabel);
                        this->Controls->Add(this->GetPushbuttonState_btn);
                        this->Controls->Add(this->ToggleLED_btn);
                        this->Controls->Add(this->Connect_btn);
                        this->Name = L"Form1";
                        this->Text = L"Simple WinUSB Demo";
                        this->ResumeLayout(false);
                        this->PerformLayout();

                }
#pragma endregion
        private:
                System::Void Connect_btn_Click(System::Object^  sender, System::EventArgs^  e)  //connectボタンが押された場合
                {

                        /* 
                        まずわれわれはアプリケーションをUSBデバイスを検出して、接続しなければならない。USBバスにはたくさんの
                        デバイスが接続されているが、デバイスの選択はユニークなベンダーIDとプロダクトIDで行なわれる。
                        Before we can "connect" our application to our USB embedded device, we must first find the device.
                        A USB bus can have many devices simultaneously connected, so somehow we have to find our device, and only
                        our device.  This is done with the Vendor ID (VID) and Product ID (PID).  Each USB product line should have
                        a unique combination of VID and PID. 

                        マイクロソフトからはUSBデバイスに係る下記関数がMSDNライブラリの中に提供されている。
                        Microsoft has created a number of functions which are useful for finding plug and play devices.  Documentation
                        for each function used can be found in the MSDN library.  We will be using the following functions:

                        SetupDiGetClassDevs()                                   //provided by setupapi.dll, which comes with Windows
                        SetupDiEnumDeviceInterfaces()                   //provided by setupapi.dll, which comes with Windows
                        GetLastError()                                                  //provided by kernel32.dll, which comes with Windows
                        SetupDiDestroyDeviceInfoList()                  //provided by setupapi.dll, which comes with Windows
                        SetupDiGetDeviceInterfaceDetail()               //provided by setupapi.dll, which comes with Windows
                        SetupDiGetDeviceRegistryProperty()              //provided by setupapi.dll, which comes with Windows
                        malloc()                                                                //part of C runtime library, msvcrt.dll?
                        CreateFile()                                                    //provided by kernel32.dll, which comes with Windows

                        関連のデータ型は下記である。
                        We will also be using the following unusual data types and structures.  Documentation can also be found in
                        the MSDN library:

                        PSP_DEVICE_INTERFACE_DATA
                        PSP_DEVICE_INTERFACE_DETAIL_DATA
                        SP_DEVINFO_DATA
                        HDEVINFO
                        HANDLE
                        GUID


        
                */
                        

                        GUID InterfaceClassGuid = {0x58D07210, 0x27C1, 0x11DD, 0xBD, 0x0B, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66}; //GUID

                        HDEVINFO DeviceInfoTable = INVALID_HANDLE_VALUE;        //接続するUSBデバイスのハンドル
                        PSP_DEVICE_INTERFACE_DATA InterfaceDataStructure        //デバイスのインターフェース情報(GUID関連等)
                                                                = new SP_DEVICE_INTERFACE_DATA;
                        PSP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure //デバイスのインターフェース詳細情報
                                                                = new SP_DEVICE_INTERFACE_DETAIL_DATA;
                                                                                                        
                        SP_DEVINFO_DATA DevInfoData;

                        DWORD InterfaceIndex = 0;
                        DWORD StatusLastError = 0;
                        DWORD dwRegType;
                        DWORD dwRegSize;
                        DWORD StructureSize = 0;
                        PBYTE PropertyValueBuffer;
                        bool MatchFound = false;
                        DWORD ErrorStatus;
                        BOOL BoolStatus = FALSE;

                        String^ DeviceIDToFind = MY_DEVICE_ID;


                        //指定されたGUIDに適合したUSBデバイスが現存するかチェックする。現存するとハンドルが返ってくる。現存しないと0が返ってくる。
                        //First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID. 

                        DeviceInfoTable = SetupDiGetClassDevsUM(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

                        while(true)             //USBデバイスの探索
                        {
                                InterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
        
                                //USBデバイス情報取得が完了したかどうかを調べる
                                if(SetupDiEnumDeviceInterfacesUM(DeviceInfoTable, NULL, &InterfaceClassGuid, InterfaceIndex, InterfaceDataStructure))
                                {
                                        ErrorStatus = GetLastError();
                                        if(ERROR_NO_MORE_ITEMS == ErrorStatus)  //デバイス情報テーブルの終端まで到達したか?
                                        {                                                                               //デバイス発見できず.
                                                SetupDiDestroyDeviceInfoListUM(DeviceInfoTable);        //古い不要な構造の削除
                                                return;         
                                        }
                                }
                                else    //または何らかの未知のエラーが発生したのか……
                                {
                                        ErrorStatus = GetLastError();
                                        SetupDiDestroyDeviceInfoListUM(DeviceInfoTable);        //古い不要な構造の削除
                                        return; 
                                }

                                //レジストリからハードウェアIDを回復する。ハードウェアIDにはVIDとPIDも含まれる。
                
                                //適切な SP_DEVINFO_DATA構造に初期化する。この構造はSetupDiGetDeviceRegistryProperty()関数で必要となる。
                                DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
                                SetupDiEnumDeviceInfoUM(DeviceInfoTable, InterfaceIndex, &DevInfoData);

                                //最初の質問はハードウェアIDのサイズである。この結果データ用のバッファーサイズが決まる。
                                SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, NULL, 0, &dwRegSize);

                                //ハードウェアIDのバッファ設定
                                PropertyValueBuffer = (BYTE *) malloc (dwRegSize);
                                if(PropertyValueBuffer == NULL) //NULL(エラー:十分なメモリが確保できない等)の場合
                                {       //この状況を正常に回復できないので
                                        SetupDiDestroyDeviceInfoListUM(DeviceInfoTable);        //現況を破棄する
                                        return;                                 // この関数を終了:exit 
                                }

                                //ハードウェアIDの回復実施
                                //Retrieve the hardware IDs for the current device we are looking at.  PropertyValueBuffer gets filled with a 
                                //REG_MULTI_SZ (array of null terminated strings).  To find a device, we only care about the very first string in the
                                //buffer, which will be the "device ID".  The device ID is a string which contains the VID and PID
                                SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, PropertyValueBuffer, dwRegSize, NULL);

                                //ハードウェアIDがUSBデバイスIDと一致するかのチェック実施
                                //Now check if the first string in the hardware ID matches the device ID of my USB device.
                                #ifdef UNICODE
                                String^ DeviceIDFromRegistry = gcnew String((wchar_t *)PropertyValueBuffer);
                                #else
                                String^ DeviceIDFromRegistry = gcnew String((char *)PropertyValueBuffer);
                                #endif

                                free(PropertyValueBuffer);      //もはや PropertyValueBufferは必要ないので、メモリリークを避けるためメモリを開放する           
                                                                                //No longer need the PropertyValueBuffer, free the memory to prevent potential memory leaks

                                //文字列をOSのバージョンに影響を受けない、より強壮なまた可搬性のよいInvariantに変換する
                                //Convert both strings to lower case.  This makes the code more robust/portable accross OS Versions
                                DeviceIDFromRegistry = DeviceIDFromRegistry->ToLowerInvariant();        
                                DeviceIDToFind = DeviceIDToFind->ToLowerInvariant();

                                //ハードウェアICがデバイスIDと一致するかチェックする
                                //Now check if the hardware ID we are looking at contains the correct VID/PID
                                MatchFound = DeviceIDFromRegistry->Contains(DeviceIDToFind);            
                                if(MatchFound == true)  //一致した場合
                                {
                                        //WinUSBインターフェースハンドルをオープンする。まづ実際のデバイスパスが必要となる。デバイスパスは SetupDiGetDeviceInterfaceDetail()
                                        //関数により取得する。この関数は2回使用する。1回目は要求構造サイズと詳細インターフェースのバッファーサイズを取得するために使用する。
                                        //2回目は実際に構造を取得する。
                                        //Device must have been found.  Open WinUSB interface handle now.  In order to do this, we will need the actual device path first.
                                        //We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice:  The first
                                        //time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually 
                                        //get the structure (after we have allocated enough memory for the structure.)
                                
                                        //サイズの特定(1回目)
                                        //First call populates "StructureSize" with the correct value
                                        DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
                                        //メモリの割り当て(1回目)
                                        //Allocate enough memory
                                        SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, NULL, NULL, &StructureSize, NULL);   
                                        DetailedInterfaceDataStructure = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(malloc(StructureSize));             
                                
                                
                                        if(DetailedInterfaceDataStructure == NULL)      //NUL(エラー)の場合:if null, error, couldn't allocate enough memory
                                        {       //Can't really recover from this situation, just exit instead.
                                                SetupDiDestroyDeviceInfoListUM(DeviceInfoTable);        //現況を破棄する Clean up the old structure we no longer need.
                                                return;         //exit  
                                        }

                                        //サイズの特定(2回目)
                                        DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
                                        //メモリの割り当て(2回目)
                                        //Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the goods.  
                                        SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, DetailedInterfaceDataStructure, StructureSize, NULL, NULL); 

                                        //USBデバイスハンドルの取得
                                        //We now have the proper device path, and we can finally open a device handle to the device.
                                        //WinUSB requires the device handle to be opened with the FILE_FLAG_OVERLAPPED attribute.
                                        MyDeviceHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

                                        ErrorStatus = GetLastError();   //呼び出し側のスレッドが持つ最新のエラーコードを取得
                                        if(ErrorStatus == ERROR_SUCCESS)        //エラーがなく成功の場合
                                        {
                                                //WinUsb_Initialize()関数によるWinUsbハンドルの取得
                                                //Now get the WinUSB interface handle by calling WinUsb_Initialize() and providing the device handle.
                                                BoolStatus = WinUsb_Initialize(MyDeviceHandle, &MyWinUSBInterfaceHandle);
                                                if(BoolStatus == TRUE)  //WinUsbハンドルの取得成功
                                                {
                                                        //WinUsb_WritePipe()関数 やWinUsb_ReadPipe()関数を使ってエンドポイントの読み書きができる状態です。 
                                                        //If gets here, the "MyWinUSBInterfaceHandle" was initialized successfully.
                                                                //May begin using the MyWinUSBInterfaceHandle handle in WinUsb_WritePipe() and
                                                        //WinUsb_ReadPipe() function calls now.  Those are the functions for writing/reading to
                                                        //the USB device's endpoints.

                                                        ToggleLED_btn->Enabled = true;                  //LEDボタン→Enable  Make button no longer greyed out
                                                        GetPushbuttonState_btn->Enabled = true; //スイッチの状態取得ボタン→Enable   Make button no longer greyed out
                                                        StateLabel->Enabled = true;                             //スイッチの状態取得ラベル→Enable   Make label no longer greyed out
                                                }
                                        }

                                        SetupDiDestroyDeviceInfoListUM(DeviceInfoTable);        //不要になった旧構造の破棄
                                                                                                                                        //Clean up the old structure we no longer need.
                                        return;
                                }

                                InterfaceIndex++;       //インクリメント
                                //マッチするVIDとPICを有するデバイスが見つかるか アイテムを使い尽くすまでループを繰り返す
                                //Keep looping until we either find a device with matching VID and PID, or until we run out of items.
                        }//end of while(true)   
                }


                private:
                System::Void ToggleLED_btn_Click(System::Object^  sender, System::EventArgs^  e) //LEDボタンが押された場合
                {
                         ULONG BytesWritten = 0;        //実際に書き込まれたバイト数
                         unsigned char OutputPacketBuffer[64];  //送信バッファー
                                                                                                //Allocate a memory buffer which will contain data to send to the USB device

                         OutputPacketBuffer[0] = 0x80;          //0x08は PIC側キバン上のLED点灯/消灯のコマンドである。
                                                                                        //0x80 is the "Toggle LED(s)" command in the firmware
                                                                                        //For simplicity, we will leave the rest of the buffer uninitialized, but you could put real
                                                                                        //data in it if you like.

                        //The winusb.dll file exposes a variety of useful functions, the most important of which is WinUsb_WritePipe() and
                         //WinUsb_ReadPipe().  These are the actual functions that can be used by a user mode application (such as this one)
                         //to send/receive application related data to specific endpoints on the USB device.
                         //Before we can use the WinUsb_WritePipe() and WinUsb_ReadPipe() functions, we will have to have already obtained
                         //a WINUSB_INTERFACE_HANDLE for the USB device.  This is done by the code that gets executed after clicking the
                         //Connect button.

                         //The following function call will send out 64 bytes (starting from OutputPacketBuffer[0]) to the USB device.  The
                         //data will arrive on "PipeID" 0x01, which is the endpoint 1 OUT endpoint.
                
                         //Endpoint PipeID: bottom 7 bits is the endpoint number (0-15), the MSb is the direction.  MSb = 0 for OUT endpoints, MSb = 1 for IN
                         //endpoints.  Ex: Endpoint 7 IN is "0x87", while endpoint 3 OUT is "0x03", while endpoint 1 OUT is 0x01.
                         //Note: The below function call is a blocking function until it completes, since it is not being called with 
                         //an "OVERLAPPED" structure specified.  See MSDN documentation regarding overlapped structures and asynchronous
                         //I/O operations.
                
                         //送信エンドポイントへの書き込み
                         WinUsb_WritePipe(MyWinUSBInterfaceHandle, 0x01, &OutputPacketBuffer[0], 64, &BytesWritten, NULL);  
                 }



                private: 
                System::Void GetPushbuttonState_btn_Click(System::Object^  sender, System::EventArgs^  e) 
                {
                         ULONG BytesWritten = 0;        //実際に書き込んだバイト数
                         ULONG BytesRead = 0;           //実際に読み込んだバイト数
                         unsigned char OutputPacketBuffer[64];  //送信バッファー       
                                                                                                //Allocate a memory buffer which will contain data to send to the USB device
                         unsigned char InputPacketBuffer[64];   //受信バッファー
                                                                                                //Allocate a memory buffer for the data which we will read from the USB device

                        OutputPacketBuffer[0] = 0x81;           //0x81 はPIC側キバン上のスイッチの状態取得のコマンドである。
                                                                                        //0x81 is the "Get Pushbutton State" command in the firmware
                                                                                        //For simplicity, we will leave the rest of the buffer uninitialized, but you could put real
                                                                                        //data in it if you like.
                         //The winusb.dll functions: WinUsb_WritePipe() and WinUsb_ReadPipe() can be used to write and read from
                         //specific endpoints on the USB device.  See the WDK documentation (also containined in MSDN) for other 
                         //"WinUSB User-Mode Client Support Routines" (ie: other useful functions for interacting with your USB device).

                         //送信バッファーへの書き込み
                         //To get the pushbutton state, first, we send a packet with our "Get Pushbutton State" command in it.
                         //The following call to WinUsb_WritePipe() sends 64 bytes of data to the USB device.
                         WinUsb_WritePipe(MyWinUSBInterfaceHandle, 0x01, &OutputPacketBuffer[0], 64, &BytesWritten, NULL);  
                
                         //受信バッファーの読み込み
                         //Now get the response packet from the firmware.
                         //The following call to WinUsb_ReadPipe() retrieves 64 bytes of data from the USB device.
                         WinUsb_ReadPipe(MyWinUSBInterfaceHandle, 0x81, &InputPacketBuffer[0], 64, &BytesRead, NULL);  



                        //InputPacketBuffer[0]は送信コマンドのエコーバックである。
                        //InputPacketBuffer[1]はPIC側キバン上のスイッチの状態に対するI/Oポートの値である。
                         //InputPacketBuffer[0] is an echo back of the command.
                         //InputPacketBuffer[1] contains the I/O port pin value for the pushbutton.  
                         if(InputPacketBuffer[1] == 0x01)       //スイッチの状態が0x01(OFF)の場合  
                         {
                                 StateLabel->Text = "State: Not Pressed";       // StateLabelに"State: Not Pressed"と表示する
                                        //Update the pushbutton state text label on the form, so the user can see the result 
                         }
                         else //スイッチの状態がONの場合 InputPacketBuffer[1] must have been == 0x00 instead       
                         {
                         StateLabel->Text = "State: Pressed";   // StateLabelに"State: Pressed"と表示する
                                        //Update the pushbutton state text label on the form, so the user can see the result 
                         }
                 }


        };      //end clss Form1

}       //end namespace



















 





<動作結果>

項目 PC側 PIC側
PC側からウィンドウ上のトグルスイッチを
クリックして、PIC側キバン上のLEDを
ON/OFFしているところです。
 
              
PIC側キバン上のスイッチ
ON/OFFをモニタしている
ところです。
<表示>
ONの場合:State Pressed
OFFの場合:State Not Pressed
OFFの場合  
ONの場合  

■ USB通信  PIC18F4550−PC間 汎用クラス 文字列送受信(割込み方式、液晶付) 
  PIC18F4550に液晶を接続した USB通信 送受信の例を紹介します。   → PIC側ハード&ソフト

以下のソフトは、 マイクロチップ社のMCHPFSUSB Framework 2.4をダウンロードして解凍後PCにできる …\Microchip Solutions\USB Device - WinUSB - Generic Driver Demo\WinUSB Simple Demo - PC Application - MS VC++ 2005 Express\Codeフォルダに収納されているVC++Cのサンプルプログラムを参考にして作成したものです。


<試作品仕様>
 ・PC側からデータをPIC側にUSB 汎用クラス通信で送信する。
 ・ドラバーはWinUSBドライバーを使用する。
 ・送信文字コードはシフトJISを使用する
 ・PIC側では受信した文字列を液晶上段に、受信データに基づき返信したデータを液晶下段に表示する。
 ・PC側でも受信したデータをリストボックスに表示する。
 ・PC側からの送信データ 及びPIC側からの返信データは以下とする。
     @ U.K.       →  London
     A America    →  Washington
     B Japan      →  0x938c(東)0x8b9e(京)
     C I am a boy.   →  Pardon ?


<プログラム 例1>
Form1.h抜粋
   <プログラム例>
#pragma once
#include <Windows.h>    //Definitions for various common and not so common types like DWORD, PCHAR, HANDLE, etc.
#include <setupapi.h>   //From Windows Server 2003 R2 Platform SDK.  Untested with Windows SDK.  Setupapi.h provides
                                                //definitions needed for the SetupDixxx() functions, which we use to find our plug and 
                                                //play device.  If getting build errors when trying to compile this project, make sure the
                                                //platform SDK is correctly installed, and that it has been integrated into the VC++ development
                                                //environment.  In other words, make sure the include paths have been setup correctly in this IDE.
                                                //Microsoft created some small tutorial videos showing how to do this.
                                                //These may be located at:
                                                //http://msdn.microsoft.com/en-us/visualc/aa336415.aspx 
                                                //The link is functional as of 21 May 2008, but could move.  If the above link does not work,
                                                //try searching for "Video1_PSDK.wmv", which may be associated with:
                                                //"Using Visual C++ Express Edition and the Platform SDK"
                                                //Also see the below comments on how to add directories to the include path.


                                                //IMPORTANT: READ THIS BEFORE TRYING TO BUILD THIS CODE
                                                //----------------------------------------------------
#include <Winusb.h>             //Winusb.h comes as a part of the Windows Driver Kit (WDK) build 6001.18001 (and presumably later versions).
                                                //The WDK is currently a free download from http://connect.microsoft.com.  Please follow these steps in order
                                                //  to find the WDK.
                                                //  1) go to http://connect.microsoft.com
                                                //  2) click the sign-in button.  If you already have a Windows Live ID then sign in with that.
                                                //              If not then create one now.
                                                //      3) Once signed in it should bring you to your dashboard.  Click on "CONNECTION DIRECTORY"
                                                //  4) find the WDK on the list.
                                                //  5) Click "Apply Now".  This should bring you to the WDK home page
                                                //  6) Click "Downloads" on the left menu bar
                                                //  7) Click on "WDK for Server 2008"
                                                //You will need the WDK build 6001.18001 (the latest currently available 19 May 2008) installed on
                                                //your computer to use this source code.  Once you have it installed, you will also need to add
                                                //the include path directories from the WDK to your VC++ IDE.  This can be done by clicking
                                                //Tools-->Options-->+Projects and Solutions-->VC++ Directories-->Show Directories for: "Include files"
                                                //Then click the Folder icon (new line) and then the "..." button and add these directories:
                                                //C:\WINDDK\6001.18001\inc\ddk
                                                //C:\WINDDK\6001.18001\inc\api
                                                //The above directory locations assume the default location for the WDK.
                                                //If the above procedure is not followed correctly, a variety of build errors looking for various
                                                //files such as winusbio.h, usb200.h, usb100.h, usb.h, etc. will occur.

//<参考>WDK関連の設定について
//★ WDKドライバーをつかってコンパイルするには以下のファイルをインクルードする必要がある。各ファイルはWDK(Windows Drivers Kit)の中にあり、
//マイクロソフトのサイトからダウンロードして PCにセットアップする必要がある。マイクロチップのUSB関連のファイルと全く同名のファイルも
//あるのでインクルードに際しては注意する必要がある。
// usb.h  usb100.h  usb200.h   winusb.h  etc.
//(注) WINDOWS.Hのあるc:\WinDDK\6001.18002\inc\apiフォルダをインクルードバスに設定すると大量コンパイルエラーがでたので、このフォルダへの
// パス設定はやめ、このフォルダ内にあったusb.h  usb100.h  usb200.h はc:\WinDDK\6001.18002\inc\ddkフォルダにコピーしてコンパイルを
//実施した。


#include <msclr/marshal.h>

                                
#define MY_DEVICE_ID  "Vid_04d8&Pid_0053"       //ベンダーIDとプロダクトIDの定義


namespace Capital {

        using namespace System;
        using namespace System::ComponentModel;
        using namespace System::Collections;
        using namespace System::Windows::Forms;
        using namespace System::Data;
        using namespace System::Drawing;

        using namespace System::Runtime::InteropServices;       //アンマネージドコードを使用する場合は必要 
                                                                                                                //Need this to support "unmanaged" code.
        using namespace System::Text;//エンコードクラスを使用する場合要追加

using namespace msclr::interop;



                
//      .NETのマネージド環境でも、アンマネージドの関数を使用するには.dllをインポートすればよい。
//      In order to use these unmanaged functions from within the managed .NET environment, we need
//      to explicitly import the functions which we will be using from other .DLL file(s).  Simply
//      including the appropriate header files is not enough. 

//      これらのアンマネージド関数には、最後に"UM" (unmanaged) をつけて命名した。これは、MSDNの中に
//      Note: In order to avoid potential name conflicts in the header files (which we still use),
//      I have renamed the functions by adding "UM" (unmanaged) onto the end of them.  To find 
//      documentation for the functions in MSDN, search for the function name without the extra 
//      "UM" attached.

//      関数がユニコードでできているのか、Ansiコードでできているかコンパイラが判別して、
//      条件付きコンパイルができるようにできるように下記プリプロセッサを記載しておく。
//      Note2: In the header files (such as setupapi.h), normally the function names are 
//      remapped, depending upon if UNICODE is defined or not.  For example, two versions of the
//      function SetupDiGetDeviceInterfaceDetail() exist.  One for UNICODE, and one for ANSI.  
//      If the wrong version of the function is called, things won't work correctly.  Therefore,
//      in order to make sure the correct one gets called (based on your compiler settings, which
//      may or may not define "UNICODE"), it is useful to explicity specify the CharSet when doing
//      the DLL import.
        

        #ifdef UNICODE
        #define Seeifdef        Unicode
        #else
        #define Seeifdef        Ansi    //特に指定がない場合はAnsiコードとみなす
        #endif

        //<以下 setupapi.h、Winusb.h により参照できる関数>

        // 指定GUID(注)のUSBデバイスがあるか調べる関数
        // (1つのデバイスのハンドルが返される)
        //(注)GUID(Globally Unique Identifier:グーイッド):グローバル識別子(2の128乗個の乱数からなる)
        //Returns a HDEVINFO type for a device information set (WinUSB devices in
        //our case).  We will need the HDEVINFO as in input parameter for calling many of
        //the other SetupDixxx() functions.
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetClassDevs")]             
        extern "C" HDEVINFO  SetupDiGetClassDevsUM(
                LPGUID  ClassGuid,      //グローバル識別子 Input: Supply the class GUID here. 
                PCTSTR  Enumerator,     //NULL Input: Use NULL here, not important for our purposes
                HWND  hwndParent,       //NULL Input: Use NULL here, not important for our purposes
                DWORD  Flags);  //デバイス情報セットの構築に使われる制御オプションInput: Flags describing what kind of filtering to use.



        //USBデバイスのインタフェース情報を取得する関数
        //Gives us "PSP_DEVICE_INTERFACE_DATA" which contains the Interface specific GUID (different
        //from class GUID).  We need the interface GUID to get the device path.
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInterfaces")]                             
        extern "C" WINSETUPAPI BOOL WINAPI  SetupDiEnumDeviceInterfacesUM(
                HDEVINFO  DeviceInfoSet,                        //Input: Give it the HDEVINFO we got from SetupDiGetClassDevs()
                PSP_DEVINFO_DATA  DeviceInfoData,       //Input (optional)
                LPGUID  InterfaceClassGuid,                     //Input 
                DWORD  MemberIndex,                                     //Input: "Index" of the device you are interested in getting the path for.
                PSP_DEVICE_INTERFACE_DATA  DeviceInterfaceData);//Output: This function fills in an "SP_DEVICE_INTERFACE_DATA" structure.


        //USBデバイスのメモリを破棄し、デバイスの情報を破棄する関数
        //SetupDiDestroyDeviceInfoList() frees up memory by destroying a DeviceInfoList
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiDestroyDeviceInfoList")]
        extern "C" WINSETUPAPI BOOL WINAPI  SetupDiDestroyDeviceInfoListUM(                     
                HDEVINFO  DeviceInfoSet);                       //Input: Give it a handle to a device info list to deallocate from RAM.


        //USBデバイス情報取得が完了したかどうかを調べる関数
        //SetupDiEnumDeviceInfo() fills in an "SP_DEVINFO_DATA" structure, which we need for SetupDiGetDeviceRegistryProperty()
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInfo")]
        extern "C" WINSETUPAPI BOOL WINAPI  SetupDiEnumDeviceInfoUM(
                HDEVINFO  DeviceInfoSet,
                DWORD  MemberIndex,
                PSP_DEVINFO_DATA  DeviceInfoData);


        //USBデバイスのプロパティを確認する関数
        //SetupDiGetDeviceRegistryProperty() gives us the hardware ID, which we use to check to see if it has matching VID/PID
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceRegistryProperty")]
        extern "C"      WINSETUPAPI BOOL WINAPI  SetupDiGetDeviceRegistryPropertyUM(
                HDEVINFO  DeviceInfoSet,
                PSP_DEVINFO_DATA  DeviceInfoData,
                DWORD  Property,
                PDWORD  PropertyRegDataType,
                PBYTE  PropertyBuffer,   
                DWORD  PropertyBufferSize,  
                PDWORD  RequiredSize);


        //USBデバイスのインターフェース詳細について調べる関数
        //SetupDiGetDeviceInterfaceDetail() gives us a device path, which is needed before CreateFile() can be used.
        [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceInterfaceDetail")]
        extern "C" BOOL SetupDiGetDeviceInterfaceDetailUM(
                HDEVINFO DeviceInfoSet,                                                                         //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs()
                PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,                          //Input: Pointer to an structure which defines the device interface.  
                PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData,     //Output: Pointer to a strucutre, which will contain the device path.
                DWORD DeviceInterfaceDetailDataSize,                                            //Input: Number of bytes to retrieve.
                PDWORD RequiredSize,                                                                            //Output (optional): Te number of bytes needed to hold the entire struct 
                PSP_DEVINFO_DATA DeviceInfoData);                                                       //Output


        //USBデバイスの初期化をおこなう関数
        //WinUsb_Initialize() needs to be called before the application can begin sending/receiving data with the USB device.
        [DllImport("winusb.dll" , CharSet = CharSet::Seeifdef, EntryPoint="WinUsb_Initialize")]
        extern "C" BOOL WinUsb_Initialize(
                HANDLE  DeviceHandle,
                PWINUSB_INTERFACE_HANDLE InterfaceHandle);

        //USBデバイスとのパイプ(エンドポイント)に書き込む関数
        //WinUsb_WritePipe() is the basic function used to write data to the USB device (sends data to OUT endpoints on the device)
        [DllImport("winusb.dll" , CharSet = CharSet::Seeifdef, EntryPoint="WinUsb_WritePipe")]
        extern "C" BOOL WinUsb_WritePipe(
                WINUSB_INTERFACE_HANDLE InterfaceHandle,
                UCHAR PipeID,
                PUCHAR Buffer,
                ULONG BufferLength,
                PULONG LengthTransferred,
                LPOVERLAPPED Overlapped);

        //USBデバイスとのパイプ(エンドポイント)からデータを読み込む関数
        //WinUsb_ReadPipe() is the basic function used to read data from the USB device (polls for and obtains data from
        //IN endpoints on the device)
        [DllImport("winusb.dll" , CharSet = CharSet::Seeifdef, EntryPoint="WinUsb_ReadPipe")]
        extern "C" BOOL WinUsb_ReadPipe(
                WINUSB_INTERFACE_HANDLE InterfaceHandle,        //The interface handle that WinUsb_Initialize returned. 
                UCHAR PipeID,   //An 8-bit value that consists of a 7-bit address and a direction bit.
                                                //This parameter corresponds to the bEndpointAddress field in the endpoint descriptor. 

                PUCHAR Buffer,  //A caller-allocated buffer that receives the data that is read. 

                ULONG BufferLength,     //The maximum number of bytes to read. This number must be less than or equal to the size, in bytes, of Buffer. 

                PULONG LengthTransferred,       //A pointer to a ULONG variable that receives the actual number of bytes that were copied into Buffer. 

                LPOVERLAPPED Overlapped);       //An optional pointer to an OVERLAPPED structure that is used for asynchronous operations. If this
                                                                        //parameter is specified, WinUsb_ReadPipe returns immediately rather than waiting synchronously 
                                                                        //for the operation to complete before returning. An event is signaled when the operation is complete. 



//グローバル変数
        HANDLE MyDeviceHandle = INVALID_HANDLE_VALUE;           //USBデバイス用のハンドル
        WINUSB_INTERFACE_HANDLE MyWinUSBInterfaceHandle;        //インターフェース用ハンドル
                                                                                                                //And then can call WinUsb_Initialize() to get the interface handle
                                                                                                                //which is needed for doing other operations with the device (like
                                                                                                                //reading and writing to the USB device).





        public ref class Form1 : public System::Windows::Forms::Form
        {
        public:
                Form1(void)
                {
                        InitializeComponent();
                        comboBox1->Items->Clear();
                        comboBox1->Items->Insert(0,"U.K.            ");
                        comboBox1->Items->Insert(1,"America         ");
                        comboBox1->Items->Insert(2,"Japan           ");
                        comboBox1->Items->Insert(3,"I am a boy.           ");
                        comboBox1->SelectedIndex = 0;

                        SendBtn->Enabled = false;       //送信ボタンをディスイネーブル化

                }

        private: 
                System::Void ConnectBtn_Click(System::Object^  sender, System::EventArgs^  e)
                {
                                        
        //              まずわれわれはアプリケーションをUSBデバイスを検出して、接続しなければならない。USBバスにはたくさんの
        //              デバイスが接続されているが、デバイスの選択はユニークなベンダーIDとプロダクトIDで行なわれる。
        //              Before we can "connect" our application to our USB embedded device, we must first find the device.
        //              A USB bus can have many devices simultaneously connected, so somehow we have to find our device, and only
        //              our device.  This is done with the Vendor ID (VID) and Product ID (PID).  Each USB product line should have
        //              a unique combination of VID and PID. 

        //              マイクロソフトからはUSBデバイスに係る下記関数がMSDNライブラリの中に提供されている。
        //              Microsoft has created a number of functions which are useful for finding plug and play devices.  Documentation
        //              for each function used can be found in the MSDN library.  We will be using the following functions:

        //              SetupDiGetClassDevs()                                   //provided by setupapi.dll, which comes with Windows
        //              SetupDiEnumDeviceInterfaces()                   //provided by setupapi.dll, which comes with Windows
        //              GetLastError()                                                  //provided by kernel32.dll, which comes with Windows
        //              SetupDiDestroyDeviceInfoList()                  //provided by setupapi.dll, which comes with Windows
        //              SetupDiGetDeviceInterfaceDetail()               //provided by setupapi.dll, which comes with Windows
        //              SetupDiGetDeviceRegistryProperty()              //provided by setupapi.dll, which comes with Windows
        //              malloc()                                                                //part of C runtime library, msvcrt.dll?
        //              CreateFile()                                                    //provided by kernel32.dll, which comes with Windows

        //              関連のデータ型は下記である。
        //              We will also be using the following unusual data types and structures.  Documentation can also be found in
        //              the MSDN library:

        //              PSP_DEVICE_INTERFACE_DATA
        //              PSP_DEVICE_INTERFACE_DETAIL_DATA
        //              SP_DEVINFO_DATA
        //              HDEVINFO
        //              HANDLE
        //              GUID

                        

                        GUID InterfaceClassGuid = {0x58D07210, 0x27C1, 0x11DD, 0xBD, 0x0B, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66}; //GUID

                        HDEVINFO DeviceInfoTable = INVALID_HANDLE_VALUE;        //接続するUSBデバイスのハンドル
                        PSP_DEVICE_INTERFACE_DATA InterfaceDataStructure        //デバイスのインターフェース情報(GUID関連等)
                                                                = new SP_DEVICE_INTERFACE_DATA;
                        PSP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure //デバイスのインターフェース詳細情報
                                                                = new SP_DEVICE_INTERFACE_DETAIL_DATA;
                                                                                                        
                        SP_DEVINFO_DATA DevInfoData;

                        DWORD InterfaceIndex = 0;
                        DWORD StatusLastError = 0;
                        DWORD dwRegType;
                        DWORD dwRegSize;
                        DWORD StructureSize = 0;
                        PBYTE PropertyValueBuffer;
                        bool MatchFound = false;
                        DWORD ErrorStatus;
                        BOOL BoolStatus = FALSE;

                        String^ DeviceIDToFind = MY_DEVICE_ID;


                        //指定されたGUIDに適合したUSBデバイスが現存するかチェックする。現存するとハンドルが返ってくる。現存しないと0が返ってくる。
                        //First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID. 

                        DeviceInfoTable = SetupDiGetClassDevsUM(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

                        while(true)             //USBデバイスの探索
                        {
                                InterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
        
                                //USBデバイス情報取得が完了したかどうかを調べる
                                if(SetupDiEnumDeviceInterfacesUM(DeviceInfoTable, NULL, &InterfaceClassGuid, InterfaceIndex, InterfaceDataStructure))
                                {
                                        ErrorStatus = GetLastError();
                                        if(ERROR_NO_MORE_ITEMS == ErrorStatus)  //デバイス情報テーブルの終端まで到達したか?
                                        {                                                                               //デバイス発見できず.
                                                SetupDiDestroyDeviceInfoListUM(DeviceInfoTable);        //古い不要な構造の削除
                                                return;         
                                        }
                                }
                                else    //または何らかの未知のエラーが発生したのか……
                                {
                                        ErrorStatus = GetLastError();
                                        SetupDiDestroyDeviceInfoListUM(DeviceInfoTable);        //古い不要な構造の削除
                                        return; 
                                }

                                //レジストリからハードウェアIDを回復する。ハードウェアIDにはVIDとPIDも含まれる。
                
                                //適切な SP_DEVINFO_DATA構造に初期化する。この構造はSetupDiGetDeviceRegistryProperty()関数で必要となる。
                                DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
                                SetupDiEnumDeviceInfoUM(DeviceInfoTable, InterfaceIndex, &DevInfoData);

                                //最初の質問はハードウェアIDのサイズである。この結果データ用のバッファーサイズが決まる。
                                SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, NULL, 0, &dwRegSize);

                                //ハードウェアIDのバッファ設定
                                PropertyValueBuffer = (BYTE *) malloc (dwRegSize);
                                if(PropertyValueBuffer == NULL) //NULL(エラー:十分なメモリが確保できない等)の場合
                                {       //この状況を正常に回復できないので
                                        SetupDiDestroyDeviceInfoListUM(DeviceInfoTable);        //現況を破棄する
                                        return;                                 // この関数を終了:exit 
                                }

                                //ハードウェアIDの回復実施
                                //Retrieve the hardware IDs for the current device we are looking at.  PropertyValueBuffer gets filled with a 
                                //REG_MULTI_SZ (array of null terminated strings).  To find a device, we only care about the very first string in the
                                //buffer, which will be the "device ID".  The device ID is a string which contains the VID and PID
                                SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, PropertyValueBuffer, dwRegSize, NULL);

                                //ハードウェアIDがUSBデバイスIDと一致するかのチェック実施
                                //Now check if the first string in the hardware ID matches the device ID of my USB device.
                                #ifdef UNICODE
                                String^ DeviceIDFromRegistry = gcnew String((wchar_t *)PropertyValueBuffer);
                                #else
                                String^ DeviceIDFromRegistry = gcnew String((char *)PropertyValueBuffer);
                                #endif

                                free(PropertyValueBuffer);      //もはや PropertyValueBufferは必要ないので、メモリリークを避けるためメモリを開放する           
                                                                                //No longer need the PropertyValueBuffer, free the memory to prevent potential memory leaks

                                //文字列をOSのバージョンに影響を受けない、より強壮なまた可搬性のよいInvariantに変換する
                                //Convert both strings to lower case.  This makes the code more robust/portable accross OS Versions
                                DeviceIDFromRegistry = DeviceIDFromRegistry->ToLowerInvariant();        
                                DeviceIDToFind = DeviceIDToFind->ToLowerInvariant();

                                //ハードウェアICがデバイスIDと一致するかチェックする
                                //Now check if the hardware ID we are looking at contains the correct VID/PID
                                MatchFound = DeviceIDFromRegistry->Contains(DeviceIDToFind);            
                                if(MatchFound == true)  //一致した場合
                                {
                                        //WinUSBインターフェースハンドルをオープンする。まづ実際のデバイスパスが必要となる。デバイスパスは SetupDiGetDeviceInterfaceDetail()
                                        //関数により取得する。この関数は2回使用する。1回目は要求構造サイズと詳細インターフェースのバッファーサイズを取得するために使用する。
                                        //2回目は実際に構造を取得する。
                                        //Device must have been found.  Open WinUSB interface handle now.  In order to do this, we will need the actual device path first.
                                        //We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice:  The first
                                        //time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually 
                                        //get the structure (after we have allocated enough memory for the structure.)
                                
                                        //サイズの特定(1回目)
                                        //First call populates "StructureSize" with the correct value
                                        DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
                                        //メモリの割り当て(1回目)
                                        //Allocate enough memory
                                        SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, NULL, NULL, &StructureSize, NULL);   
                                        DetailedInterfaceDataStructure = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(malloc(StructureSize));             
                                
                                
                                        if(DetailedInterfaceDataStructure == NULL)      //NUL(エラー)の場合:if null, error, couldn't allocate enough memory
                                        {       //Can't really recover from this situation, just exit instead.
                                                SetupDiDestroyDeviceInfoListUM(DeviceInfoTable);        //現況を破棄する Clean up the old structure we no longer need.
                                                return;         //exit  
                                        }

                                        //サイズの特定(2回目)
                                        DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
                                        //メモリの割り当て(2回目)
                                        //Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the goods.  
                                        SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, DetailedInterfaceDataStructure, StructureSize, NULL, NULL); 

                                        //USBデバイスハンドルの取得
                                        //We now have the proper device path, and we can finally open a device handle to the device.
                                        //WinUSB requires the device handle to be opened with the FILE_FLAG_OVERLAPPED attribute.
                                        MyDeviceHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

                                        ErrorStatus = GetLastError();   //呼び出し側のスレッドが持つ最新のエラーコードを取得
                                        if(ErrorStatus == ERROR_SUCCESS)        //エラーがなく成功の場合
                                        {
                                                //WinUsb_Initialize()関数によるWinUsbハンドルの取得
                                                //Now get the WinUSB interface handle by calling WinUsb_Initialize() and providing the device handle.
                                                BoolStatus = WinUsb_Initialize(MyDeviceHandle, &MyWinUSBInterfaceHandle);
                                                if(BoolStatus == TRUE)  //WinUsbハンドルの取得成功
                                                {
                                                        //WinUsb_WritePipe()関数 やWinUsb_ReadPipe()関数を使ってエンドポイントの読み書きができる状態です。 
                                                        //If gets here, the "MyWinUSBInterfaceHandle" was initialized successfully.
                                                                //May begin using the MyWinUSBInterfaceHandle handle in WinUsb_WritePipe() and
                                                        //WinUsb_ReadPipe() function calls now.  Those are the functions for writing/reading to
                                                        //the USB device's endpoints.

                                                        listBox1->Items->Clear();
                                                        label3->Text = "接続完了";
                                                        SendBtn->Enabled = true;        //送信ボタン→イネーブル
                                                        label3->ForeColor = Color::Red; // ”送信”ラベル色→赤色
                                
                                                }
                                        }

                                        SetupDiDestroyDeviceInfoListUM(DeviceInfoTable);        //不要になった旧構造の破棄
                                                                                                                                        //Clean up the old structure we no longer need.
                                        return;
                                }

                                InterfaceIndex++;       //インクリメント
                                //マッチするVIDとPICを有するデバイスが見つかるか アイテムを使い尽くすまでループを繰り返す
                                //Keep looping until we either find a device with matching VID and PID, or until we run out of items.
                        }//end of while(true)   
                }


        private:
                System::Void SendBtn_Click(System::Object^  sender, System::EventArgs^  e) //送信ボタンをクリックした場合
                {

                        int i;
                        ULONG BytesWritten = 0; //実際に書き込まれたバイト数
                        array <Byte>^ bytesArry;        //マネージ形式の配列の宣言
                        Encoding^ encSjis = Encoding::GetEncoding("shift-jis");   //シフトJISのエンコードクラスを宣言
                        Encoding^ encUni = Encoding::GetEncoding("unicode");  //ユニコード(UCS-16)のエンコードクラスを宣言
                        unsigned char strSend_C[64];
                        char strReceive_C[64];
                        
                        //unsigned char* message ;

                        ULONG BytesRead = 0;            //実際に読み込んだバイト数
                        unsigned char InputPacketBuffer[64];    //受信バッファー
                

                        String^ strSend = comboBox1->Text;
                        if (!strSend->EndsWith("\r")) strSend += "\r";  //改行が無かったら \rを追加する
                        bytesArry = encSjis->GetBytes( strSend ); //'送信文字をunicode→Shift-jisに変換してをByte配列に格納
                        
                        for(i = 0; i < bytesArry->Length - 1 ; i++)strSend_C[i] = bytesArry[i];         //マネージ形式の配列をC言語の配列に変換


                        WinUsb_WritePipe(MyWinUSBInterfaceHandle, 0x01, strSend_C, 64, &BytesWritten, NULL);    //送信エンドポイントへの書き込み
                        
                        WinUsb_ReadPipe(MyWinUSBInterfaceHandle, 0x81, (unsigned char*)strReceive_C, 64, &BytesRead, NULL);             //受信エンドポイントの読み込み 
 

       /*       //C言語の配列(ネイティブ型)をマネージ型配列にマーシャリングする方法もある
        
                        for(i = 0; i < 17; i++)
                        {
                                if(strReceive_C[i] == '\r')
                                {
                                        strReceive_C[i] = 0x00; //文字列化
                                        break;
                                }
                        }

        

                        char* message = strReceive_C;   //C言語文字列

                        String^ strReceive = marshal_as<String^>( message );    //ネイティブ型(C言語)文字列をマネージ型にマーシャリング
                        listBox1->Items->Add(strReceive);
                
      */





                          for(i = 0; i < 17;  i++)
                         {
                                 bytesArry[i] = strReceive_C[i];                //マネージ型配列作成
                         }

                         
                          array<Byte>^byteUni = Encoding::Convert(encSjis, encUni, bytesArry); //shift-jis からunicodeに変換する
                          String^ strUni = encUni->GetString(byteUni); //配列をunicodeの文字列に変換

                          
                        listBox1->Items->Add(strUni);

                }

                
                ~Form1()
                {
                        if (components)
                        {
                                delete components;
                        }
                }
        private: System::Windows::Forms::Button^  ConnectBtn;

        protected: 

        private: System::Windows::Forms::Label^  label1;
        private: System::Windows::Forms::ComboBox^  comboBox1;
        private: System::Windows::Forms::ListBox^  listBox1;
        private: System::Windows::Forms::Label^  label2;
private: System::Windows::Forms::Button^  SendBtn;
private: System::Windows::Forms::Label^  label3;

        private:
                /* <summary>
                 必要なデザイナ変数です。
                 </summary>*/
                System::ComponentModel::Container ^components;

#pragma region Windows Form Designer generated code
                /// <summary>
                /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
                /// コード エディタで変更しないでください。
                /// </summary>
                void InitializeComponent(void)
                {
                        this->ConnectBtn = (gcnew System::Windows::Forms::Button());
                        this->label1 = (gcnew System::Windows::Forms::Label());
                        this->comboBox1 = (gcnew System::Windows::Forms::ComboBox());
                        this->listBox1 = (gcnew System::Windows::Forms::ListBox());
                        this->label2 = (gcnew System::Windows::Forms::Label());
                        this->SendBtn = (gcnew System::Windows::Forms::Button());
                        this->label3 = (gcnew System::Windows::Forms::Label());
                        this->SuspendLayout();
                        // 
                        // ConnectBtn
                        // 
                        this->ConnectBtn->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 9.75F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, 
                                static_cast<System::Byte>(128)));
                        this->ConnectBtn->Location = System::Drawing::Point(12, 12);
                        this->ConnectBtn->Name = L"ConnectBtn";
                        this->ConnectBtn->Size = System::Drawing::Size(75, 23);
                        this->ConnectBtn->TabIndex = 0;
                        this->ConnectBtn->Text = L"接続";
                        this->ConnectBtn->UseVisualStyleBackColor = true;
                        this->ConnectBtn->Click += gcnew System::EventHandler(this, &Form1::ConnectBtn_Click);
                        // 
                        // label1
                        // 
                        this->label1->AutoSize = true;
                        this->label1->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 9.75F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, 
                                static_cast<System::Byte>(128)));
                        this->label1->Location = System::Drawing::Point(146, 44);
                        this->label1->Name = L"label1";
                        this->label1->Size = System::Drawing::Size(69, 13);
                        this->label1->TabIndex = 2;
                        this->label1->Text = L"送信データ";
                        // 
                        // comboBox1
                        // 
                        this->comboBox1->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 9.75F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, 
                                static_cast<System::Byte>(128)));
                        this->comboBox1->FormattingEnabled = true;
                        this->comboBox1->Location = System::Drawing::Point(116, 59);
                        this->comboBox1->Name = L"comboBox1";
                        this->comboBox1->Size = System::Drawing::Size(121, 21);
                        this->comboBox1->TabIndex = 3;
                        // 
                        // listBox1
                        // 
                        this->listBox1->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 9.75F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, 
                                static_cast<System::Byte>(128)));
                        this->listBox1->FormattingEnabled = true;
                        this->listBox1->Location = System::Drawing::Point(271, 59);
                        this->listBox1->Name = L"listBox1";
                        this->listBox1->Size = System::Drawing::Size(120, 95);
                        this->listBox1->TabIndex = 4;
                        // 
                        // label2
                        // 
                        this->label2->AutoSize = true;
                        this->label2->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 9.75F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, 
                                static_cast<System::Byte>(128)));
                        this->label2->Location = System::Drawing::Point(306, 23);
                        this->label2->Name = L"label2";
                        this->label2->Size = System::Drawing::Size(69, 13);
                        this->label2->TabIndex = 5;
                        this->label2->Text = L"受信データ";
                        // 
                        // SendBtn
                        // 
                        this->SendBtn->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 9.75F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, 
                                static_cast<System::Byte>(128)));
                        this->SendBtn->Location = System::Drawing::Point(128, 12);
                        this->SendBtn->Name = L"SendBtn";
                        this->SendBtn->Size = System::Drawing::Size(75, 23);
                        this->SendBtn->TabIndex = 6;
                        this->SendBtn->Text = L"送信";
                        this->SendBtn->UseVisualStyleBackColor = true;
                        this->SendBtn->Click += gcnew System::EventHandler(this, &Form1::SendBtn_Click);
                        // 
                        // label3
                        // 
                        this->label3->AutoSize = true;
                        this->label3->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 12, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, 
                                static_cast<System::Byte>(128)));
                        this->label3->Location = System::Drawing::Point(23, 44);
                        this->label3->Name = L"label3";
                        this->label3->Size = System::Drawing::Size(59, 16);
                        this->label3->TabIndex = 7;
                        this->label3->Text = L"未接続";
                        // 
                        // Form1
                        // 
                        this->AutoScaleDimensions = System::Drawing::SizeF(6, 12);
                        this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
                        this->ClientSize = System::Drawing::Size(403, 235);
                        this->Controls->Add(this->label3);
                        this->Controls->Add(this->SendBtn);
                        this->Controls->Add(this->label2);
                        this->Controls->Add(this->listBox1);
                        this->Controls->Add(this->comboBox1);
                        this->Controls->Add(this->label1);
                        this->Controls->Add(this->ConnectBtn);
                        this->Name = L"Form1";
                        this->Text = L"Form1";
                        this->ResumeLayout(false);
                        this->PerformLayout();

                }
#pragma endregion


};
}



<動作結果>

  PC側Window PIC側液晶画面
<送信前>
<送信後>

”接続ボタン”をクリック後順次下記をコンボビックスから選択して送信した結果
@ U..K.
A Amerrica
B Japan
C I am a boy.

(注) Japanを送信した時にPIC側から実際に返信されるデータは、
0x938c(東)0x8b9e(京)です。 ffはコンパイラC18のバグ(?)か、制御文字の場合の仕様によるもので実際には送信データに含まれていません。