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

    作成2014.03.22

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

  1. Device - CDC - Basic Demo(Microsoft Visual Basic 2010 Express)のソースプログラム
    **Device - CDC - Basic Demo(Microsoft Visual Basic 2010 Express)のソースプログラムを以下に示しますが、基本的にはRS 232Cの通信サンプルプログラムと同じであることがわかります。
    Imports System.IO.Ports
    Public Class Form1
        Delegate Sub SetTextCallback(ByVal [text] As String)
        Private Sub UpdateCOMPortList()
            Dim s As String
            Dim i As Integer
            Dim foundDifference As Boolean
    
            i = 0
            foundDifference = False
    
            If lstCOMPorts.Items.Count = SerialPort.GetPortNames().Length Then
                For Each s In SerialPort.GetPortNames()
    
                    If lstCOMPorts.Items(i).Equals(s) = False Then
                        foundDifference = True
                    End If
                    i = i + 1
                Next s
            Else
                foundDifference = True
            End If
            If foundDifference = False Then
                Exit Sub
            End If
            lstCOMPorts.Items.Clear()
            For Each s In SerialPort.GetPortNames()
                lstCOMPorts.Items.Add(s)
            Next s
            lstCOMPorts.SelectedIndex = 0
        End Sub
    
    
        Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
            UpdateCOMPortList()
        End Sub
    
        Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnect.Click
    
            Try
                SerialPort1.PortName = lstCOMPorts.Items(lstCOMPorts.SelectedIndex).ToString()
    
                SerialPort1.Open()
                btnConnect.Enabled = False
                lstCOMPorts.Enabled = False
                btnClose.Enabled = True
    
                txtDataReceived.Clear()
                txtDataReceived.AppendText("Connected." + vbCrLf)
            Catch ex As Exception
                btnClose_Click(Me, e)
    
            End Try
        End Sub
    
        Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
            btnClose.Enabled = False
            btnConnect.Enabled = True
            lstCOMPorts.Enabled = True
    
            Try
                SerialPort1.DiscardInBuffer()
                SerialPort1.DiscardOutBuffer()
                SerialPort1.Close()
            Catch ex As Exception
            End Try
        End Sub
    
    
        Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
            Try
                SetText(SerialPort1.ReadExisting())
            Catch ex As Exception
                btnClose_Click(Me, e)
            End Try
    
        End Sub
    
        Private Sub SetText(ByVal [text] As String)
            If txtDataReceived.InvokeRequired Then
                Dim d As New SetTextCallback(AddressOf SetText)
                Invoke(d, New Object() {[text]})
            Else
                txtDataReceived.AppendText(text)
            End If
        End Sub
    
    
        Private Sub btnSendData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSendData.Click
            Try
                SerialPort1.Write(txtData.Text)
            Catch ex As Exception
                btnClose_Click(Me, e)
            End Try
        End Sub
    End Class
    


  2. SYSTEM_Initialize(SYSTEM_STATE_USB_START); RS232C通信サンプルプログラム
    **RS232 using thread-safe calls to Windows Forms controlsサンプルプログラムの詳細説明は以下のアドレスを参照願います。
    http://www.codeproject.com/Articles/17261/RS-using-thread-safe-calls-to-Windows-Forms-con#

     以下にサンプルプログラムを示します。(コメントは削除しました)
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.IO.Ports;
    namespace RS232
    {
        public partial class fclsRS232Tester : Form
        {
            string InputData = String.Empty;
            delegate void SetTextCallback(string text);
            public fclsRS232Tester()
            {
                InitializeComponent();
                string[] ports = SerialPort.GetPortNames();
                foreach (string port in ports)
                {
                    cmbComSelect.Items.Add(port);
                }
            }
            private void cmbComSelect_SelectionChangeCommitted(object sender,  EventArgs e)
            {
                if (port.IsOpen) port.Close();
                port.PortName = cmbComSelect.SelectedItem.ToString();
                stsStatus.Text = port.PortName + ": 9600,8N1";
                try
                {
                    port.Open();
                }
                catch
                {
                    MessageBox.Show("Serial port " + port.PortName + 
                       MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    cmbComSelect.SelectedText = "";
                    stsStatus.Text = "Select serial port!";
                }
            }
    
            private void btnSend_Click(object sender, EventArgs e)
            {
                if (port.IsOpen) port.WriteLine(txtOut.Text);
                else MessageBox.Show("Serial port is closed!", 
                                     "RS232 tester", 
                                     MessageBoxButtons.OK, 
                                     MessageBoxIcon.Error);
                txtOut.Clear();
            }
            private void btnClear_Click(object sender, EventArgs e)
            {
                txtIn.Clear();
            }
            private void port_DataReceived_1(object sender, 
                    SerialDataReceivedEventArgs e)
            {
                InputData = port.ReadExisting();
                if (InputData != String.Empty)
                {
                    SetText(InputData);
                }
            }
    
            private void SetText(string text)
            {
                if (this.txtIn.InvokeRequired)
                {
                    SetTextCallback d = new SetTextCallback(SetText);
                    this.Invoke(d, new object[] { text });
                }
                else this.txtIn.Text += text;
            }
    
        }
    }
    


  3. Device - CDC - Basic DemoのPC側ソースプログラムの検討結果
    (1)基本的なコードはRS232C通信サンプルプログラムと同一です。
    (2)サンプルプログラムで最も難解なのは
    Delegate Sub SetTextCallback(ByVal [text] As String)
    Private Sub SetText(ByVal [text] As String)
            If txtDataReceived.InvokeRequired Then
                Dim d As New SetTextCallback(AddressOf SetText)
                Invoke(d, New Object() {[text]})
            Else
                txtDataReceived.AppendText(text)
            End If
        End Sub
    ですがRS232C通信サンプルプログラムでも同様な記載があります。
    delegate void SetTextCallback(string text);
    private void SetText(string text)
    {
                if (this.txtIn.InvokeRequired)
                {
                    SetTextCallback d = new SetTextCallback(SetText);
                    this.Invoke(d, new object[] { text });
                }
                else this.txtIn.Text += text;
    }
    が記載されています。
    (3)上記コードの意味は
    http://www.codeproject.com/Articles/17261/RS-using-thread-safe-calls-to-Windows-Forms-con#
    を参照するとよいと思います。
    (3)これ以外にもRS232C通信サンプルプログラムは数多くあると思われます。
    (4)CDCモードでは、RS232C通信サンプルプログラムが使えることになります。
    


  4. PICマイコン側メインルーチン
    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_DeviceCDCBasicDemoTasks();//カスタムルーチン usb_device_hid.
        }
    }
    *システム初期化、USB初期化、USB接続を最初に実行します。
    *カスタムルーチンを無限ループで繰り返します。
    


  5. SYSTEM_Initialize(SYSTEM_STATE_USB_START); //システム初期化
    void SYSTEM_Initialize( SYSTEM_STATE state )
    {
        switch(state)
        {
            case SYSTEM_STATE_USB_START://これが選択
                LED_Enable(LED_USB_DEVICE_STATE);//LED1を出力設定
                BUTTON_Enable(BUTTON_DEVICE_CDC_BASIC_DEMO);//SW2を入力設定
                break;		
            case SYSTEM_STATE_USB_SUSPEND: 
                break;
            case SYSTEM_STATE_USB_RESUME:
                break;
        }
    }
    *LED1を出力設定、SW2を入力設定のみです。
    *この部分の理解は容易です。
    


  6. USBDeviceInit(); //USB初期化
    void USBDeviceInit(void)
    {
        uint8_t i;
    
        USBDisableInterrupts();
        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;        
        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;
    }
    **Device - HID - Custom Demosと同一です。
    **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)
    **Device - HID - Custom Demosと同一です。
    **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
        CDC_DEVICE,             // Class Code
        0x00,                   // Subclass code
        0x00,                   // Protocol code
        USB_EP0_BUFF_SIZE,      // Max packet size for EP0, see usb_config.h
        0x04D8,                 // Vendor ID
        0x000A,                 // Product ID: CDC RS-232 Emulation 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=0x000Aということは理解できます。
    **グローバル変数は非常に多く定義されていますが、それらがどのように使用されるかは不明です。
    


  9. APP_DeviceCDCBasicDemoTasks(); //カスタムルーチン usb_device_hid.c
    void APP_DeviceCDCBasicDemoTasks()
    {
        if(BUTTON_IsPressed(BUTTON_DEVICE_CDC_BASIC_DEMO) == true)
        {
            if(buttonPressed == false) //SW2が押された最初のみ
            {
                if(mUSBUSARTIsTxTrfReady() == true) //バッファが使用可能の場合
                {
                    putrsUSBUSART(buttonMessage); //buttonMessageを送信
                    buttonPressed = true;
                }
            }
        }
        else
        {
            buttonPressed = false; //フラグクリア
        }
        if( USBUSARTIsTxTrfReady() == true) //バッファが使用可能の場合
        {
            uint8_t i;
            uint8_t numBytesRead;
    
            numBytesRead = getsUSBUSART(readBuffer, sizeof(readBuffer)); //受信
            for(i=0; i 0)
            {
                putUSBUSART(writeBuffer,numBytesRead); //writeBufferを送信
            }
        }
        CDCTxService();
    }
    **受信と送信のおおまかなシーケンスは容易に理解できます。
    **受信と送信の内部処理はブラックボックスです。
    **ブラックボックスの部分を除いて、プログラムのカスタマイズは可能と思われます。
    


  10. getsUSBUSART(readBuffer, sizeof(readBuffer)); //受信
    uint8_t getsUSBUSART(uint8_t *buffer, uint8_t len)
    {
        cdc_rx_len = 0;
        if(!USBHandleBusy(CDCDataOutHandle)) //受信可能の場合
        {
            if(len > USBHandleGetLength(CDCDataOutHandle)) //readBufferサイズが大きい場合
                len = USBHandleGetLength(CDCDataOutHandle);
            for(cdc_rx_len = 0; cdc_rx_len < len; cdc_rx_len++)
                buffer[cdc_rx_len] = cdc_data_rx[cdc_rx_len]; //readBufferにコピー
            CDCDataOutHandle = USBRxOnePacket(CDC_DATA_EP,(uint8_t*)&cdc_data_rx,sizeof(cdc_data_rx));//受信
        }
        return cdc_rx_len;//受信文字数を返す
    


  11. putUSBUSART(writeBuffer,numBytesRead); //writeBufferを送信
    void putUSBUSART(uint8_t *data, uint8_t  length)
    {
        USBMaskInterrupts(); //USB Interrupt Enable bit = Disabled;
        if(cdc_trf_state == CDC_TX_READY) //送信可能の場合
        {
            mUSBUSARTTxRam((uint8_t*)data, length);     // データ送信 See cdc.h
        }
        USBUnmaskInterrupts(); //USB Interrupt Enable bit = Enabled;
    }
    


  12. 検討まとめ
    (1)CDCモードでは、パソコン側の処理がRS232Cと同一となる。
    (2)このため、HIDモードと比較してサンプルプログラムの内容を理解しやすい。
    (3)USB通信機能の詳細まで理解することが目的でなければ、USB通信機能の基本部分はブラックボックスとしたまま扱える。
    (4)USB通信機能の基本部分を変更することなく、使用目的に応じたプログラムのカスタマイズは可能である。
    (5)プログラムは学問ではなく、実用的に使えるか?とういことが重要と考えるならば、サンプルプログラムは十分実用的に使えることになります。
    (6)USB通信機能の基本部分全てを十分理解してプログラミングしたいと考えるならば、実現までに膨大な時間を消費することになると思います。
    (7)全てを理解しなくても、サンプルプログラムは十分動作するし、カスタマイズも可能です。











15章:I2C仕様EEPROM評価回路に行く。

トップページに戻る。