作成2014.03.22
Device - CDC - Basic Demoソースプログラムの内容はかなり難し
く詳細までは理解できないのですが、部分的に検討してみたいと思います。
- 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
- 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;
}
}
}
- 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通信サンプルプログラムが使えることになります。
- 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接続を最初に実行します。
*カスタムルーチンを無限ループで繰り返します。
- 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を入力設定のみです。
*この部分の理解は容易です。
- 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初期化の基本部分で変更の必要は無いと思われます。
- 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接続の基本部分で変更の必要は無いと思われます。
- 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ということは理解できます。
**グローバル変数は非常に多く定義されていますが、それらがどのように使用されるかは不明です。
- 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();
}
**受信と送信のおおまかなシーケンスは容易に理解できます。
**受信と送信の内部処理はブラックボックスです。
**ブラックボックスの部分を除いて、プログラムのカスタマイズは可能と思われます。
- 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;//受信文字数を返す
- 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;
}
- 検討まとめ
(1)CDCモードでは、パソコン側の処理がRS232Cと同一となる。
(2)このため、HIDモードと比較してサンプルプログラムの内容を理解しやすい。
(3)USB通信機能の詳細まで理解することが目的でなければ、USB通信機能の基本部分はブラックボックスとしたまま扱える。
(4)USB通信機能の基本部分を変更することなく、使用目的に応じたプログラムのカスタマイズは可能である。
(5)プログラムは学問ではなく、実用的に使えるか?とういことが重要と考えるならば、サンプルプログラムは十分実用的に使えることになります。
(6)USB通信機能の基本部分全てを十分理解してプログラミングしたいと考えるならば、実現までに膨大な時間を消費することになると思います。
(7)全てを理解しなくても、サンプルプログラムは十分動作するし、カスタマイズも可能です。