这次的目标是实现一个 USB 转串口的设备,参考的是Arduino Leonardo 的 USB CDC。这个串口是标准USB串口,在Windows 下无需驱动。首先抓取描述符如下:
USB Composite Device
Connection Status | Device connected |
Current Configuration | 1 |
Speed | Full (12 Mbit/s) |
Device Address | 4 |
Number Of Open Pipes | 3 |
Device Descriptor Arduino Leonardo
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 12h | |
1 | bDescriptorType | 1 | 01h | Device |
2 | bcdUSB | 2 | 0200h | USB Spec 2.0 |
4 | bDeviceClass | 1 | EFh | Miscellaneous |
5 | bDeviceSubClass | 1 | 02h | Common Class |
6 | bDeviceProtocol | 1 | 01h | Interface Association Descriptor |
7 | bMaxPacketSize0 | 1 | 40h | 64 bytes |
8 | idVendor | 2 | 2341h | |
10 | idProduct | 2 | 8036h | |
12 | bcdDevice | 2 | 0100h | 1.00 |
14 | iManufacturer | 1 | 01h | "Arduino LLC" |
15 | iProduct | 1 | 02h | "Arduino Leonardo" |
16 | iSerialNumber | 1 | 03h | |
17 | bNumConfigurations | 1 | 01h |
Configuration Descriptor 1 Bus Powered, 500 mA
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 09h | |
1 | bDescriptorType | 1 | 02h | Configuration |
2 | wTotalLength | 2 | 004Bh | |
4 | bNumInterfaces | 1 | 02h | |
5 | bConfigurationValue | 1 | 01h | |
6 | iConfiguration | 1 | 00h | |
7 | bmAttributes | 1 | A0h | Bus Powered, Remote Wakeup |
4..0: Reserved | ...00000 | |||
5: Remote Wakeup | ..1..... | Yes | ||
6: Self Powered | .0...... | No, Bus Powered | ||
7: Reserved (set to one) (bus-powered for 1.0) | 1....... | |||
8 | bMaxPower | 1 | FAh | 500 mA |
Interface Association Descriptor Abstract Control Model
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 08h | |
1 | bDescriptorType | 1 | 0Bh | Interface Association |
2 | bFirstInterface | 1 | 00h | |
3 | bInterfaceCount | 1 | 02h | |
4 | bFunctionClass | 1 | 02h | CDC Control |
5 | bFunctionSubClass | 1 | 02h | Abstract Control Model |
6 | bFunctionProtocol | 1 | 00h | |
7 | iFunction | 1 | 00h |
Interface Descriptor 0/0 CDC Control, 1 Endpoint
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 09h | |
1 | bDescriptorType | 1 | 04h | Interface |
2 | bInterfaceNumber | 1 | 00h | |
3 | bAlternateSetting | 1 | 00h | |
4 | bNumEndpoints | 1 | 01h | |
5 | bInterfaceClass | 1 | 02h | CDC Control |
6 | bInterfaceSubClass | 1 | 02h | Abstract Control Model |
7 | bInterfaceProtocol | 1 | 00h | |
8 | iInterface | 1 | 00h |
Offset | Field | Size | Value | Description |
0 | bFunctionLength | 1 | 05h | |
1 | bDescriptorType | 1 | 24h | CS Interface |
2 | bDescriptorSubtype | 1 | 00h | Header |
3 | bcdCDC | 2 | 0110h | 1.10 |
Call Management Functional Descriptor
Offset | Field | Size | Value | Description |
0 | bFunctionLength | 1 | 05h | |
1 | bDescriptorType | 1 | 24h | CS Interface |
2 | bDescriptorSubtype | 1 | 01h | Call Management |
3 | bmCapabilities | 1 | 01h | |
7..2: Reserved | 000000.. | |||
1: Data Ifc Usage | ......0. | Call management only over Comm Ifc | ||
0: Call Management | .......1 | Handles call management itself | ||
4 | bDataInterface | 1 | 01h |
Abstract Control Management Functional Descriptor
Offset | Field | Size | Value | Description |
0 | bFunctionLength | 1 | 04h | |
1 | bDescriptorType | 1 | 24h | CS Interface |
2 | bDescriptorSubtype | 1 | 02h | Abstract Control Management |
3 | bmCapabilities | 1 | 06h | |
7..4: Reserved | 0000.... | |||
3: Connection | ....0... | |||
2: Send Break | .....1.. | Send Break request supported | ||
1: Line Coding | ......1. | Line Coding requests and Serial State notification supported | ||
0: Comm Features | .......0 |
Offset | Field | Size | Value | Description |
0 | bFunctionLength | 1 | 05h | |
1 | bDescriptorType | 1 | 24h | CS Interface |
2 | bDescriptorSubtype | 1 | 06h | Union |
3 | bControlInterface | 1 | 00h | |
4 | bSubordinateInterface0 | 1 | 01h | CDC Data |
Endpoint Descriptor 81 1 In, Interrupt, 64 ms
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 07h | |
1 | bDescriptorType | 1 | 05h | Endpoint |
2 | bEndpointAddress | 1 | 81h | 1 In |
3 | bmAttributes | 1 | 03h | Interrupt |
1..0: Transfer Type | ......11 | Interrupt | ||
7..2: Reserved | 000000.. | |||
4 | wMaxPacketSize | 2 | 0010h | 16 bytes |
6 | bInterval | 1 | 40h | 64 ms |
Interface Descriptor 1/0 CDC Data, 2 Endpoints
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 09h | |
1 | bDescriptorType | 1 | 04h | Interface |
2 | bInterfaceNumber | 1 | 01h | |
3 | bAlternateSetting | 1 | 00h | |
4 | bNumEndpoints | 1 | 02h | |
5 | bInterfaceClass | 1 | 0Ah | CDC Data |
6 | bInterfaceSubClass | 1 | 00h | |
7 | bInterfaceProtocol | 1 | 00h | |
8 | iInterface | 1 | 00h |
Endpoint Descriptor 02 2 Out, Bulk, 64 bytes
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 07h | |
1 | bDescriptorType | 1 | 05h | Endpoint |
2 | bEndpointAddress | 1 | 02h | 2 Out |
3 | bmAttributes | 1 | 02h | Bulk |
1..0: Transfer Type | ......10 | Bulk | ||
7..2: Reserved | 000000.. | |||
4 | wMaxPacketSize | 2 | 0040h | 64 bytes |
6 | bInterval | 1 | 00h |
Endpoint Descriptor 83 3 In, Bulk, 64 bytes
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 07h | |
1 | bDescriptorType | 1 | 05h | Endpoint |
2 | bEndpointAddress | 1 | 83h | 3 In |
3 | bmAttributes | 1 | 02h | Bulk |
1..0: Transfer Type | ......10 | Bulk | ||
7..2: Reserved | 000000.. | |||
4 | wMaxPacketSize | 2 | 0040h | 64 bytes |
6 | bInterval | 1 | 00h |
This report was generated by USBlyzer
实现了上面的描述符之后,就能保证插入系统后 Windows 设备管理器上不会出现惊叹号。Windows 支持的标准 CDC 动作有下面8个【参考1】
- SET_LINE_CODING 用于主机对设备设置波特率,停止位,奇偶校验和位数
- GET_LINE_CODING用于主机取得设备当前波特率,停止位,奇偶校验和位数
- SET_CONTROL_LINE_STATE 用于产生 RS-232/V.24 标准的控制信号
- SEND_BREAK
- SERIAL_STATE 返回状态信息,比如:奇偶校验错误
- SEND_ENCAPSULATED_COMMAND
- GET_ENCAPSULATED_RESPONSE
- RESPONSE_AVAILABLE
从实际验证的结果看起来(就是前面提到的使用 Arduino Leonardo 作为验证对象),实现 1-3 的支持外加 2个Endpoint Bulk传输即可实现通讯。
1.SET_LINE_CODING 的实现。收到 bRequestType ==0x21, bRequest== SET_LINE_CODING 即可判定这个操作;之后用 ENDPOINT 0 的OUT 中返回当前的LineInfo;最后再通过 ENDPOINT 0 的 IN 返回0字节
2. GET_LINE_CODING 的实现。收到 bRequestType ==0xA1, bRequest== GET_LINE_CODING 即可判定这个操作;之后直接返回当前的LineInfo;最后再通过 ENDPOINT 0 的 IN 返回0字节
3. SET_CONTROL_LINE_STATE 的实现。收到 bRequestType ==0x21, bRequest== 0x22 即可判定这个操作;之后直接通过ENDPOINT 0 的 IN 返回0字节。
实现上面的操作之后,即可使用串口工具打开设备产生的串口了。接下来实现串口传输的模拟:
- 从Windows(HOST) 对CH567通过串口工具发送数据。数据会出现在 endpoint2 OUT上,我们将收到的数据送到CH567的串口上,然后再通过一个额外的串口转USB即可看到。具体代码是:
if(intstatus == (UIS_TOKEN_OUT|2)) /* endpoint 2 下传 */
{
if(R8_USB1_INT_ST&bUIS_TOG_OK)
{
// 下传是 HOST -> DEVICE
// 用串口工具打开设备对应的串口,然输入的内容可以在 Debug 串口上看到
for (i=0; i<R16_USB1_RX_LEN; i++)
{
printf("%X ",UsbEp2OUTBuf[i]);
}
printf("\n");
}
}
2.从CH567定时对 Windows 发送字符串,使用串口工具打开CH567端口后可以看到这个字符串。修改有2处,第一个是发送的代码,在main.c 中每隔5秒发送一次:
while(1)
{
mDelaymS(5000);
if (UsbConfig!=0)
{
memcpy( UsbEp3INBuf, &Msg[0], sizeof( Msg ));
R16_UEP3_T_LEN1 = sizeof( Msg );
R8_UEP3_TX_CTRL1 = (R8_UEP3_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
}
};
另外一处是当CH567收到 Endpoint3 IN 中断时,使用0字节来回复给主机
if(intstatus == (UIS_TOKEN_IN|3)) /* endpoint 3 上传 */
{
R16_UEP3_T_LEN1 = 0;
R8_UEP3_TX_CTRL1 = (R8_UEP3_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
}
此外,还有一处需要特别注意的是:必须使用高波特率用于 printf 的串口输出(>1Mhz),实验中我使用的是 CH343 6Mhz的波特率,否则会发生丢失log的情况(实际上有跑到代码,但是对应那句话的 Log 不出现,这个问题我调试了2天,在USB 逻辑分析仪上看到了发送的数据包,但是串口 Log说没有)。
运行结果如下,左侧是用于调试的CH343产生的串口,右边是CH567模拟出来的串口。当我们对CH567发送”1234567”时,CH567收到后会从UART再次送出,因此我们在左侧能看到;此外,CH567每隔5秒发送一次”www.lab-z.com”字符串在右侧窗口可以看到。
完整代码下载:
参考:
1. https://www.silabs.com/documents/public/application-notes/AN758.pdf