USB CDC实现 虚拟串口

USB CDC实现 虚拟串口
现在很多电脑已经不带232了,特别是手提电脑。这使很多使用手提在外调试人员非常不方便。或许你可以买一条市面上usb转232转换线,但这些线抗干扰不是太好,在一些干扰大的地方会发生连接中断的问题,所以往往要加光耦隔离器。在一些高端的单片机一般带USB接口,如果可以使用这些接口做一个USB的虚拟com口将会非常实用,但是使用USB一般要编写上位机驱程。没有windows驱程编写基础的朋友一般很为难。其实微软的CDC类中含有这一类USB转com的类,Windows也自带这方面的驱程。就像市面上的usb鼠标,u盘一样,windows里面已经有了他们的驱动函数库,只要我们按照windows的CDC描述来配置USB就可以了。
关于CDC的类描述类容请参考Universal Serial Bus Class Definitions for Communication Devices 的PDF文件。它是下面所有内容的纲领。
下面讲解下制作步骤:
1:把对应的描述符发送出去:下面是发送内容
const USB_DEVICE_DESCRIPTOR DeviceDescr =
{
// device descriptor
0x12,
DESC_DEVICE,
LE_WORD(0x0101), // bcdUSB
0x02, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
MAX_PACKET_SIZE0, // bMaxPacketSize
//设备ID
LE_WORD(0xFFFF), // idVendor
LE_WORD(0x0005), // idProduct
LE_WORD(0x0100), // bcdDevice
/* 不要使用原来的ID
0x71,0x04, // PHILIPS公司的设备ID Vendor ID = PHILIPS Semiconductor,Inc.
0x78,0x23, // 设备制造商定的产品ID Product ID
0x00,0x01, // 设备系列号 Device release number in binary-coded decimal
*/
0x01, // iManufacturer
0x02, // iProduct
0x03, // iSerialNumber
0x01, // bNumConfigurations
};
const USB_DESCRIPTOR usb_descr =
{
// configuration descriptor
{
0x09,
DESC_CONFIGURATION,
LE_WORD(67), // wTotalLength
0x02, // bNumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration
0xC0, // bmAttributes
0x32 // bMaxPower
},
// control class interface
{
0x09,
DESC_INTERFACE, // 4
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndPoints
0x02, // bInterfaceClass
0x02, // bInterfaceSubClass
// 0x01, // bInterfaceProtocol, linux requires value of 1 for the cdc_acm module
0x00, // bInterfaceProtocol, linux requires value of 1 for the cdc_acm module
0x00 // iInterface
},
// header functional descriptor
0x05,
CS_INTERFACE,
0x00,
LE_WORD(0x0110),
// call management functional descriptor
0x05,
CS_INTERFACE,
0x01,
0x01, // bmCapabilities = device handles call management
0x01, // bDataInterface
// ACM functional descriptor
0x04,
CS_INTERFACE,
0x02,
0x02, // bmCapabilities
// union functional descriptor
0x05,
CS_INTERFACE,
0x06,
0x00, // bMasterInterface
0x01, // bSlaveInterface0
// notification EP
0x07,
DESC_ENDPOINT,
INT_IN_EP, // bEndpointAddress
0x03, // bmAttributes = intr
LE_WORD(8), // wMaxPacketSize
0x0A, //

bInterval
// data class interface descriptor
0x09,
DESC_INTERFACE,
0x01, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndPoints
0x0A, // bInterfaceClass = data
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface
// data EP OUT
0x07,
DESC_ENDPOINT,
BULK_OUT_EP, // bEndpointAddress
0x02, // bmAttributes = bulk
LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize
0x00, // bInterval
// data EP in
0x07,
DESC_ENDPOINT,
BULK_IN_EP, // bEndpointAddress
0x02, // bmAttributes = bulk
LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize
0x00, // bInterval
// string descriptors
0x04,
DESC_STRING,
LE_WORD(0x0409),
0x0E,
DESC_STRING,
'L', 0, 'P', 0, 'C', 0, 'U', 0, 'S', 0, 'B', 0,
0x14,
DESC_STRING,
'U', 0, 'S', 0, 'B', 0, 'S', 0, 'e', 0, 'r', 0, 'i', 0, 'a', 0, 'l', 0,
0x12,
DESC_STRING,
'D', 0, 'E', 0, 'A', 0, 'D', 0, 'C', 0, '0', 0, 'D', 0, 'E', 0,
// terminating zero
0
};
这里要注意的是USB_DEVICE_DESCRIPTOR里面的设备ID不要使用原来的厂家ID,按照上面给出的,这个值对应INF文件里的:
[GSerialDeviceList]
%GSERIAL%=GSerialInstall, USB\VID_FFFF&PID_0005
PID和VID
每种USB设备都有一个PID和VID。VID是生产商的代号,PID是产品的代号,每个代号都是一个双字节的整数。PID和VID不能随意设置,它是由USB标准协会进行分配的,就像IP地址的分配一样。一个USB的PID/VID许可需要花费1500美元,在很多情况下,特别对小公司是一个很大的费用。针对这种情况,有免费的PID/VID对,分别适用于HID类、CDC类和通用类设备,使用AVRUSB的用户可以免费使用它们,这样对于大多数应用来说就不用再自己去申请PID/VID了。
PID和VID在驱动程序和用户程序中都将用到,它是windows识别USB设备的关键参数,用户程序也需要通过PID和VID来查找相应的USB设备。
它是windows用来识别usb设备,和查找对应驱程的标识号。如果枚举成功,它会提示安装驱程。
它是windows用来识别usb设备,和查找对应驱程的标识号。如果枚举成功,它会提示安装驱程。
所以要识别为CDC类设备,请使用上面给出的ID,而不要使用厂商ID。可能你不想改变原来USB设备的ID,而把INF文件的[GSerialDeviceList]改为和原来的厂商ID,这样我也试过,可以识别出设备,
但是不能在com8里面出现com的参数设置项。(下面的端口设置不会出现),而且出现的COM8不能操作,就是不能在串口助手使用com8.
使用程序给出的配置符才能识别为com,而且出现的端口才能使用。我的解释是这个ID对应CDC的免费PID和VID,所以windows能正确配置设备。
关于枚举过程,你可以参考我给出的资料,或者自己搜索,这里我不再叙述。调试过程可以使用bus_hound进行一步步跟踪调试,或者在单片机程序加入printf语句(通过232口)来跟

踪单片机的USB和window的通信数据流。
下面是我采用第二种方法,在单片机固件程序安插printf语句,使用串口助手截获的数据,供大家参考用。
step_ packet表示收到step包;req代表上位机发来的请求数据,ANS表示返回数据,如果ANS=为空代表返回一个空包。
step_ packet
req=80,6,0,1,0,0,40,0,
ANS=12,1,1,1,2,0,0,40,ff,ff,5,0,0,1,1,2,3,1,
ANS=
step_ packet
req=0,5,1,0,0,0,0,0,
ANS=
ANS=
step_ packet
req=80,6,0,1,0,0,12,0,
ANS=12,1,1,1,2,0,0,40,ff,ff,5,0,0,1,1,2,3,1,
ANS=
step_ packet
req=80,6,0,2,0,0,9,0,
ANS=9,2,43,0,2,1,0,c0,32,
ANS=
step_ packet
req=80,6,0,3,0,0,ff,0,
step_ packet
req=80,6,0,2,0,0,ff,0,
ANS=9,2,43,0,2,1,0,c0,32,9,4,0,0,1,2,2,0,0,5,24,0,10,1,5,24,1,1,1,4,24,2,2,5,24,6,0,1,7,5,81,3,8,0,a,9,4,1,0,2,a,0,0,0,7,5,2,2,c0,0,0,7,5,82,2,
ANS=c0,0,0,4,3,9,4,e,3,4c,0,50,0,43,0,55,0,53,0,42,0,14,3,55,0,53,0,42,0,53,0,65,0,72,0,69,0,61,0,6c,0,12,3,44,0,45,0,41,0,44,0,43,0,30,0,44,0,45,0,0,
ANS=
step_ packet
req=80,6,0,3,0,0,ff,0,
step_ packet
req=80,6,0,3,0,0,ff,0,
step_ packet
req=80,6,0,1,0,0,12,0,
ANS=12,1,1,1,2,0,0,40,ff,ff,5,0,0,1,1,2,3,1,
ANS=
step_ packet
req=80,6,0,2,0,0,9,1,
ANS=9,2,43,0,2,1,0,c0,32,9,4,0,0,1,2,2,0,0,5,24,0,10,1,5,24,1,1,1,4,24,2,2,5,24,6,0,1,7,5,81,3,8,0,a,9,4,1,0,2,a,0,0,0,7,5,2,2,c0,0,0,7,5,82,2,
ANS=c0,0,0,4,3,9,4,e,3,4c,0,50,0,43,0,55,0,53,0,42,0,14,3,55,0,53,0,42,0,53,0,65,0,72,0,69,0,61,0,6c,0,12,3,44,0,45,0,41,0,44,0,43,0,30,0,44,0,45,0,0,
ANS=
step_ packet
req=0,9,1,0,0,0,0,0,
ANS=
ANS=
step_ packet
req=a1,21,0,0,0,0,7,0,
ANS=80,25,0,0,0,1,7,
ANS=
step_ packet
req=21,22,0,0,0,0,0,0,
ANS=
ANS=
2:关于inf文件和sys驱动
.inf文件可用提供模板进行快速定制。 文件格式非常简单,大多数Windows程序员都能快速读懂。 即使对于不太熟悉的人,也可以很快识别中文件由不同部分组成,每个部分的标题包含在方括号中,在相应部分内有一或多个由参数名称和参数值组成的正文。 在整个文件中,只能少数几个部分需要用户定制,包括:
?[Device List] 部分包括从USB-IF获得(许可)的公司和应用独有的VID/PID号码对以及其它信息。
?[Strings]部分包含操作系统在即插即用阶段在不同的对话框中使用的不同字符串和标识,以及在硬件管理器中标识设备的字符串和标识。
本文提供的inf文件不需要进行修改,但用户可以对?[Strings]部分进行修改,可以改变设备列表的设备描述字符串。
[Strings]
LINUX = "spxwh"
GSERIAL = "FX2N PLC COM"
GSERIAL_DISPLAY_NAME = " FX2N PLC COM "
上面会把制造商显示为spxwh, 设备描述为FX2N PLC COM (三菱PLC仿真COM口)
.sys文件要从C:\WINDOWS\Driver Cache\i386的.cab file导出(因为windows已带这个CDC类的驱程了),但是为了方便我直接给出来了usbser.sys,注意名字不要改变,应为在inf文件内已经

进行了设置。
3:安装inf文件
如果一切顺利(第一次插入会提示安装驱程,安装提供的INF文件),此时即插即用已经完成了安装过程,一个新的串行设备就会出现在硬件管理器中。 系统会自动使用下一个可用的端口号为它指定一个名称(例如,已经安装了COM2和COM1,就会出现COM3)。 用户可以使用已经编写好用来访问实际COMx端口的程序来访问这个端口(如所有Windows软件中的超级终端程序)。 连接好后,除了通讯速度提高了,功能上没有什么区别。
参考文章与资料:
1. Universal Serial Bus Class Definitions for Communication Devices
2. 将串行应用移植到USB接口的简便方法
3. AVRUSB技术探讨

相关文档
最新文档