13章:Device - HID - Custom Demosソースプログラムの検討

    作成2014.03.21

     Device - HID - Custom Demosソースプログラムの内容はかなり難しく 詳細までは理解できないのですが、部分的に検討してみたいと思います。

  1. PnP Demo - Windows Software(Microsoft Visual Basic 2010 Express)の概要
    (1)注意事項
    *32ビットDLL機能を使用するため、64ビットモードでは動作しません。32ビット(x86)モードでビルドする必要があります。

    (2)GUIDコード
    Dim DeviceIDToFind As String = "Vid_04d8&Pid_003f"
    Vid_04d8はMicrochip、Pid_003fはHIDクラスのCustomを意味するようです。
    Dim InterfaceClassGuid As New Guid("4D1E55B2-F16F-11CF-88CB-001111000030")
    HIDクラスデバイスのためのグローバル一意識別子(GUID)とありますが、コードの詳細の意味はわかりません。


    (3) Private Sub Form1_Load
    *各種の初期設定、通信チェックが行われます。
    *最後にPrivate Sub ReadWriteThread_DoWorkルーチンを起動します。

    (4)Protected Overrides Sub WndProc
    *USB通信の可否をチェック
    *OK の場合、書き込み用ファイルと読み取り用ファイルを生成
    *最後に自分を呼び出して無限ループ

    (5)Private Function CheckIfPresentAndGetUSBDevicePath
    *コード"Vid_04d8&Pid_003f"が一致するか?を確認
    *一致した場合、メモリーに内容をコピー
    *コード"Vid_04d8&Pid_003f"が一致するか、チェックするデバイスがなくなるまで繰り返し

    (6)Private Sub ToggleLEDs_btn_Click
    *ToggleLEDsPending = Trueのみです。

    (7)Private Sub FormUpdateTimer_Tick
    *タイマー割り込みです。
    *接続、非接続の表示をします。
    *PushbuttonPressed = Falseの場合"Pushbutton State: Not Pressed"そうでない場合、"Pushbutton State: Pressed"を表示します。
    *progressBar1にADCValueを表示します。

    (8)Private Sub ReadWriteThread_DoWork
    *接続の場合、AD変換値を取得するコードをWriteHandleToUSBDeviceに書き込みます。
    *ReadFileManagedBufferに書き込みがあり、INBuffer(1) = &H37の場合、ADCValueにAD変換値をコピーします。
    *Pushbutton Stateを取得するコードをWriteHandleToUSBDeviceに書き込みます。
    *ReadFileManagedBufferに書き込みがあり、INBuffer(1) = &H81、INBuffer(2) = &H1の場合、PushbuttonPressed = Falseとします。
    *INBuffer(1) = &H81、INBuffer(2) = &H0の場合、PushbuttonPressed = Trueとします。
    *ToggleLEDsPending = Trueの場合、"Toggle LED(s)" commandWriteHandleToUSBDeviceに書き込みます。

    (9)Private Function ReadFileManagedBuffer
    *受信内容を読み出して、INBufferにコピー


  2. PnP Demo - Windows Software(Microsoft Visual Basic 2010 Express)の概要検討結果
    (1)USB通信の基本部分はsetupapi.dllに定義されている関数を使用して構築しているが、特殊な関数であり詳細は理解できない。
    (2)USB通信結果のファイル生成、書き込み、読み出しはkernel32.dllに定義されている関数を使用している。
    (3)GUIDコード= "Vid_04d8&Pid_003f"でUSB通信相手の識別を行っている。
    (4)GUIDコード="4D1E55B2-F16F-11CF-88CB-001111000030"は意味不明
    (5)INBuffer(1) = &H37がAD変換要求
    (6)INBuffer(1) = &H81がスイッチの状態要求(INBuffer(2) = &H0がオン、INBuffer(2) = &H1がオフ)
    (7)INBuffer(1) = &H80がLEDの点灯・消灯切替要求
    (8) Private Sub Form1_Loadは操作画面に関する部分のみ、カスタマイズが必要となる。
    (9)Protected Overrides Sub WndProcはUSB通信の基本部分でカスタマイズが不要
    (10)Private Function CheckIfPresentAndGetUSBDevicePathはUSB通信の基本部分でカスタマイズが不要
    (11)Private Sub ToggleLEDs_btn_Clickはボタン操作に関する処理でカスタマイズが必要
    (12)Private Sub FormUpdateTimer_Tickは一定時間ごとの処理でカスタマイズが必要
    (13)Private Sub ReadWriteThread_DoWorkはコマンド操作に関する処理でカスタマイズが必要
    (14)Private Function ReadFileManagedBufferはUSB通信の基本部分でカスタマイズが不要
    **USB通信の基本部分はサンプルプログラムを修正せずにそのまま使用します。(この部分が難解です)
    **操作に関する部分は目的に応じてカスタマイズが必要となります。
    **USB通信心臓部の処理はブラックボックスに近く難解ですが、操作に関する部分は理解可能です。


  3. MPLAB X IDE(Device - HID - Custom Demos)ソースプログラムのファイル構成
    ソースプログラムは以下の.cファイルと.hファイルで構成されています。
    C:\src\apps\usb\device\hid_custom\firmware\src\app_device_custom_hid.c
    C:\src\apps\usb\device\hid_custom\firmware\src\app_device_custom_hid.h @ 0x86;
    C:\src\apps\usb\device\hid_custom\firmware\src\app_led_usb_status.c @((unsigned)&TRISB*8)+3;
    C:\src\apps\usb\device\hid_custom\firmware\src\app_led_usb_status.h
    C:\src\apps\usb\device\hid_custom\firmware\src\main.c @((unsigned)&T2CON*8)+0;
    C:\src\apps\usb\device\hid_custom\firmware\src\system_config.h @((unsigned)&T2CON*8)+1;
    C:\src\apps\usb\device\hid_custom\firmware\src\usb_config.h
    C:\src\apps\usb\device\hid_custom\firmware\src\usb_descriptors.c
    C:\src\apps\usb\device\hid_custom\firmware\src\system_config\picdem_fs_usb\fixed_address_memory.h
    C:\src\apps\usb\device\hid_custom\firmware\src\system_config\picdem_fs_usb\io_mapping.h
    C:\src\apps\usb\device\hid_custom\firmware\src\system_config\picdem_fs_usb\system.c
    C:\src\apps\usb\device\hid_custom\firmware\src\system_config\picdem_fs_usb\system.h
    C:\src\bsp\picdem_fs_usb\adc.c
    C:\src\bsp\picdem_fs_usb\adc.h
    C:\src\bsp\picdem_fs_usb\buttons.c
    C:\src\bsp\picdem_fs_usb\buttons.h
    C:\src\bsp\picdem_fs_usb\leds.c
    C:\src\bsp\picdem_fs_usb\leds.h
    C:\src\bsp\picdem_fs_usb\power.h
    C:\src\framework\usb\usb.h
    C:\src\framework\usb\usb_ch9.h
    C:\src\framework\usb\usb_common.h
    C:\src\framework\usb\usb_device.h
    C:\src\framework\usb\usb_device_hid.h
    C:\src\framework\usb\usb_hal.h
    C:\src\framework\usb\usb_hal_pic18.h
    C:\src\framework\usb\src\usb_device.c
    C:\src\framework\usb\src\usb_device_hid.c
    C:\src\framework\usb\src\usb_device_local.h

    *多くのファイルがからみあって構成されているため、全ての関係を調べるのは困難です。
    *デバッガーの動作が思うように操作できないため、大まかなシーケンスを調査したいと思います。


  4. MAIN_RETURN(main.c)
    MAIN_RETURN main(void)
    {
        SYSTEM_Initialize(SYSTEM_STATE_USB_START); //システム初期化 system.c
        USBDeviceInit(); //USB初期化 usb_device.c
        USBDeviceAttach(); //USB接続 usb_device.c
        while(1)
        {
            SYSTEM_Tasks(); //システムタスク
            #if defined(USB_POLLING)//非アクティブ
                USBDeviceTasks(); //非アクティブ
            #endif
            if( USBGetDeviceState() < CONFIGURED_STATE )
            {
                continue;//何もしない
            }
            if( USBIsDeviceSuspended() == true )
            {
                continue;//何もしない
            }
            APP_DeviceCustomHIDTasks(); //カスタムルーチン usb_device_hid.c
        }
    }
    *システム初期化、USB初期化、USB接続を最初に実行します。
    *カスタムルーチンを無限ループで繰り返します。
    


  5. SYSTEM_Initialize(SYSTEM_STATE_USB_START); //システム初期化
    void SYSTEM_Initialize( SYSTEM_STATE state ) //system.c
    {
        switch(state)
        {
            case SYSTEM_STATE_USB_START://これが選択
                LED_Enable(LED_USB_DEVICE_STATE); //LED1を出力に設定 leds.c
                LED_Enable(LED_USB_DEVICE_HID_CUSTOM);//LED2を出力に設定 leds.c
                
                BUTTON_Enable(BUTTON_USB_DEVICE_HID_CUSTOM); //SW2を入力に設定 buttons.c
    
                 //AD許可、RA0のみアナログ、20 TAD、FOSC/4を設定adc.c
                ADC_SetConfiguration(ADC_CONFIGURATION_DEFAULT);
         
                ADC_Enable(ADC_CHANNEL_POTENTIOMETER); //RA0を入力設定adc.c
                break;
                
            case SYSTEM_STATE_USB_SUSPEND://非選択
                break;
            case SYSTEM_STATE_USB_RESUME://非選択
                break;
        }
    }
    *LED、SW、A/D変換の設定を行っています。
    *この部分の理解は容易です。
    


  6. USBDeviceInit(); //USB初期化
    void USBDeviceInit(void) //usb_device.c
    {
        uint8_t i;
        USBDisableInterrupts();//USB割り込みを無効にして初期化
        USBClearInterruptRegister(U1EIR);  //USBエラーをクリア
        USBClearInterruptRegister(U1IR);  //USB割り込みをクリア
        U1EP0 = 0; //endpoint control registersをクリア
        DisableNonZeroEndpoints(USB_MAX_EP_NUMBER);//指定のエンドポイントの制御レジスタをクリア
        SetConfigurationOptions();
        USBPowerModule(); //power up the module
        USBSetBDTAddress(BDT); //set the address of the BDT
        for(i=0;i<(sizeof(BDT)/sizeof(BDT_ENTRY));i++) //Clear all of the BDT entries
        {
            BDT[i].Val = 0x00;
        }
        USBPingPongBufferReset = 1; //Ping Pong bufferをリセット           
        U1ADDR = 0x00;    // Reset to default address               
        USBPacketDisable = 0;     // Make sure packet processing is enabled       
        USBPingPongBufferReset = 0; //Stop trying to reset ping pong buffer pointers
        while(USBTransactionCompleteIF == 1)   // Flush any pending transactions   
        {
            USBClearInterruptFlag(USBTransactionCompleteIFReg,USBTransactionCompleteIFBitNum);
            inPipes[0].info.Val = 0; //変数初期化
            outPipes[0].info.Val = 0; //変数初期化
            outPipes[0].wCount.Val = 0; //変数初期化
        }
        USBStatusStageEnabledFlag1 = true; //フラグセット
        USBStatusStageEnabledFlag2 = true; //フラグセット
        USBDeferINDataStagePackets = false; //フラグクリア
        USBDeferOUTDataStagePackets = false; //フラグクリア
        USBBusIsSuspended = false; //フラグクリア
        for(i = 0; i < (uint8_t)(USB_MAX_EP_NUMBER+1u); i++)
        {
            pBDTEntryIn[i] = 0u; //変数初期化
            pBDTEntryOut[i] = 0u; //変数初期化
            ep_data_in[i].Val = 0u; //変数初期化
            ep_data_out[i].Val = 0u; //変数初期化
        }
      //Get ready for the first packet
        pBDTEntryIn[0] = (volatile BDT_ENTRY*)&BDT[EP0_IN_EVEN];
        // Initialize EP0 as a Ctrl EP
        U1EP0 = EP_CTRL|USB_HANDSHAKE_ENABLED; 
      //Prepare for the first SETUP on EP0 OUT       
        BDT[EP0_OUT_EVEN].ADR = ConvertToPhysicalAddress(&SetupPkt);
        BDT[EP0_OUT_EVEN].CNT = USB_EP0_BUFF_SIZE;
        BDT[EP0_OUT_EVEN].STAT.Val = _DAT0|_BSTALL;
        BDT[EP0_OUT_EVEN].STAT.Val |= _USIE;
        USBActiveConfiguration = 0;  // Clear active configuration        
        USBDeviceState = DETACHED_STATE; //Indicate that we are now in the detached state
    }
    
    **この部分は難解で良く理解できません。
    **USB初期化の基本部分で変更の必要は無いと思われます。
    


  7. USBDeviceAttach(); //USB接続 usb_device.c
    #if defined(USB_INTERRUPT)
    void USBDeviceAttach(void)
    {
        //if we are in the detached state
        if(USBDeviceState == DETACHED_STATE)
        {
            if(USB_BUS_SENSE == 1)
            {
        	    //Initialize registers to known states.
                U1CON = 0;          
        
                // Mask all USB interrupts
                U1IE = 0;                                
        
                //Configure things like: pull ups, full/low-speed mode, 
                //set the ping pong mode, and set internal transceiver
                SetConfigurationOptions();
        
                USBEnableInterrupts();  //Modifies global interrupt settings
        
                // Enable module & attach to bus
                while(!U1CONbits.USBEN){U1CONbits.USBEN = 1;}
        
                //moved to the attached state
                USBDeviceState = ATTACHED_STATE;
        
                #ifdef  USB_SUPPORT_OTG
                    U1OTGCON = USB_OTG_DPLUS_ENABLE | USB_OTG_ENABLE;  
                #endif
            }
        }
    }
    #endif  //#if defined(USB_INTERRUPT)
    
    **この部分も難解で良く理解できません。
    **USB接続の基本部分で変更の必要は無いと思われます。
    


  8. USB_DEVICE_DESCRIPTOR
     Vendor IDとProduct IDはusb_descriptors.cに以下のように定義されています。
    const USB_DEVICE_DESCRIPTOR device_dsc=
    {
        0x12,    // Size of this descriptor in bytes
        USB_DESCRIPTOR_DEVICE,                // DEVICE descriptor type
        0x0200,                 // USB Spec Release Number in BCD format
        0x00,                   // Class Code
        0x00,                   // Subclass code
        0x00,                   // Protocol code
        USB_EP0_BUFF_SIZE,          // Max packet size for EP0, see usb_config.h
        0x04D8,                 // Vendor ID
        0x003F,                 // Product ID: Custom HID device demo
        0x0100,                 // Device release number in BCD format
        0x01,                   // Manufacturer string index
        0x02,                   // Product string index
        0x00,                   // Device serial number string index
        0x01                    // Number of possible configurations
    };
    
    **Vendor ID=0x04D8、Product ID=0x003Fということは理解できます。
    **グローバル変数は非常に多く定義されていますが、それらがどのように使用されるかは不明です。
    


  9. APP_DeviceCustomHIDTasks(); //カスタムルーチン usb_device_hid.c
    void APP_DeviceCustomHIDTasks()
    {   
        if(HIDRxHandleBusy(USBOutHandle) == false) //バッファが使用可能の場合
        {   
            //We just received a packet of data from the USB host.
            //Check the first uint8_t of the packet to see what command the host
            //application software wants us to fulfill.
            switch(ReceivedDataBuffer[0])
            {
                case COMMAND_TOGGLE_LED:  //Toggle LEDs commandの場合
                    LED_Toggle(LED_USB_DEVICE_HID_CUSTOM); //LED2を反転
                    break;
                case COMMAND_GET_BUTTON_STATUS:  //Get push button stateの場合
                    if(!HIDTxHandleBusy(USBInHandle)) //バッファが使用可能の場合
                    {
                        ToSendDataBuffer[0] = 0x81; //ToSendDataBuffer[0] = 0x81をセット	
                        if(BUTTON_IsPressed(BUTTON_USB_DEVICE_HID_CUSTOM) == false)	
                        { //SW2がOFFの時
                                ToSendDataBuffer[1] = 0x01; // 0x01をセット
                        }
                        else					
                        {  //SW2がONの時
                                ToSendDataBuffer[1] = 0x00; // 0x00をセット
                        }
                        //パケットを送信
                        USBInHandle = HIDTxPacket(CUSTOM_DEVICE_HID_EP, (uint8_t*)&ToSendDataBuffer[0],64);
                    }
                    break;
    
                case COMMAND_READ_POTENTIOMETER: //AD変換 commandの場合 
                    {
                        uint16_t pot;
                        if(!HIDTxHandleBusy(USBInHandle)) //バッファが使用可能の場合
                        {
                            pot = ADC_Read10bit(ADC_CHANNEL_POTENTIOMETER);//AD変換値をpotに代入
    
                            ToSendDataBuffer[0] = 0x37;  //ToSendDataBuffer[0] = 0x37をセット 
                            memcpy(&ToSendDataBuffer[1], &pot, 2);//ToSendDataBufferにpot値をコピー
                //パケットを送信
                            USBInHandle = HIDTxPacket(CUSTOM_DEVICE_HID_EP, (uint8_t*)&ToSendDataBuffer[0],64);
                        }
                    }
                    break;
            }
        //パケットを受信
            USBOutHandle = HIDRxPacket(CUSTOM_DEVICE_HID_EP, (uint8_t*)&ReceivedDataBuffer, 64);
        }
    }
    
    **パケットの受信と送信のおおまかなシーケンスは容易に理解できます。
    **パケットの受信と送信の内部処理はブラックボックスです。
    **ブラックボックスの部分を除いて、プログラムのカスタマイズは可能と思われます。
    


  10. 検討まとめ
    (1)USB通信機能は複雑であり、処理の詳細の一部はブラックボックスとなっている。
    (2)USB通信機能の詳細まで理解しようとすると迷宮に入り込む。
    (3)USB通信機能の詳細まで理解することが目的でなければ、USB通信機能の基本部分はブラックボックスとしたまま扱える。
    (4)USB通信機能の基本部分を変更することなく、使用目的に応じたプログラムのカスタマイズは可能である。
    (5)プログラムは学問ではなく、実用的に使えるか?とういことが重要と考えるならば、サンプルプログラムは十分実用的に使えることになります。
    (6)USB通信機能の基本部分全てを十分理解してプログラミングしたいと考えるならば、実現までに膨大な時間を消費することになると思います。
    (7)全てを理解しなくても、サンプルプログラムは十分動作するし、カスタマイズも可能です。











14章:Device - CDC - Basic Demoソースプログラムの検討に行く。

トップページに戻る。