使用 CH567 实现 USB1 串口

这次的目标是实现一个 USB 转串口的设备,参考的是Arduino Leonardo 的 USB CDC。这个串口是标准USB串口,在Windows 下无需驱动。首先抓取描述符如下:

USB Composite Device

Connection StatusDevice connected
Current Configuration1
SpeedFull (12 Mbit/s)
Device Address4
Number Of Open Pipes3

Device Descriptor Arduino Leonardo

OffsetFieldSizeValueDescription
0bLength112h
1bDescriptorType101hDevice
2bcdUSB20200hUSB Spec 2.0
4bDeviceClass1EFhMiscellaneous
5bDeviceSubClass102hCommon Class
6bDeviceProtocol101hInterface Association Descriptor
7bMaxPacketSize0140h64 bytes
8idVendor22341h
10idProduct28036h
12bcdDevice20100h1.00
14iManufacturer101h"Arduino LLC"
15iProduct102h"Arduino Leonardo"
16iSerialNumber103h
17bNumConfigurations101h

Configuration Descriptor 1 Bus Powered, 500 mA

OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType102hConfiguration
2wTotalLength2004Bh
4bNumInterfaces102h
5bConfigurationValue101h
6iConfiguration100h
7bmAttributes1A0hBus 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....... 
8bMaxPower1FAh500 mA

Interface Association Descriptor Abstract Control Model

OffsetFieldSizeValueDescription
0bLength108h
1bDescriptorType10BhInterface Association
2bFirstInterface100h
3bInterfaceCount102h
4bFunctionClass102hCDC Control
5bFunctionSubClass102hAbstract Control Model
6bFunctionProtocol100h
7iFunction100h

Interface Descriptor 0/0 CDC Control, 1 Endpoint

OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType104hInterface
2bInterfaceNumber100h
3bAlternateSetting100h
4bNumEndpoints101h
5bInterfaceClass102hCDC Control
6bInterfaceSubClass102hAbstract Control Model
7bInterfaceProtocol100h
8iInterface100h

Header Functional Descriptor

OffsetFieldSizeValueDescription
0bFunctionLength105h
1bDescriptorType124hCS Interface
2bDescriptorSubtype100hHeader
3bcdCDC20110h1.10

Call Management Functional Descriptor

OffsetFieldSizeValueDescription
0bFunctionLength105h
1bDescriptorType124hCS Interface
2bDescriptorSubtype101hCall Management
3bmCapabilities101h
7..2: Reserved000000.. 
1: Data Ifc Usage......0. Call management only over Comm Ifc
0: Call Management.......1 Handles call management itself
4bDataInterface101h

Abstract Control Management Functional Descriptor

OffsetFieldSizeValueDescription
0bFunctionLength104h
1bDescriptorType124hCS Interface
2bDescriptorSubtype102hAbstract Control Management
3bmCapabilities106h
7..4: Reserved0000.... 
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 

Union Functional Descriptor

OffsetFieldSizeValueDescription
0bFunctionLength105h
1bDescriptorType124hCS Interface
2bDescriptorSubtype106hUnion
3bControlInterface100h
4bSubordinateInterface0101hCDC Data

Endpoint Descriptor 81 1 In, Interrupt, 64 ms

OffsetFieldSizeValueDescription
0bLength107h
1bDescriptorType105hEndpoint
2bEndpointAddress181h1 In
3bmAttributes103hInterrupt
1..0: Transfer Type......11 Interrupt
7..2: Reserved000000.. 
4wMaxPacketSize20010h16 bytes
6bInterval140h64 ms

Interface Descriptor 1/0 CDC Data, 2 Endpoints

OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType104hInterface
2bInterfaceNumber101h
3bAlternateSetting100h
4bNumEndpoints102h
5bInterfaceClass10AhCDC Data
6bInterfaceSubClass100h
7bInterfaceProtocol100h
8iInterface100h

Endpoint Descriptor 02 2 Out, Bulk, 64 bytes

OffsetFieldSizeValueDescription
0bLength107h
1bDescriptorType105hEndpoint
2bEndpointAddress102h2 Out
3bmAttributes102hBulk
1..0: Transfer Type......10 Bulk
7..2: Reserved000000.. 
4wMaxPacketSize20040h64 bytes
6bInterval100h

Endpoint Descriptor 83 3 In, Bulk, 64 bytes

OffsetFieldSizeValueDescription
0bLength107h
1bDescriptorType105hEndpoint
2bEndpointAddress183h3 In
3bmAttributes102hBulk
1..0: Transfer Type......10 Bulk
7..2: Reserved000000.. 
4wMaxPacketSize20040h64 bytes
6bInterval100h

This report was generated by USBlyzer

实现了上面的描述符之后,就能保证插入系统后 Windows 设备管理器上不会出现惊叹号。Windows 支持的标准 CDC 动作有下面8个【参考1】

  1. SET_LINE_CODING  用于主机对设备设置波特率,停止位,奇偶校验和位数
  2. GET_LINE_CODING用于主机取得设备当前波特率,停止位,奇偶校验和位数
  3. SET_CONTROL_LINE_STATE 用于产生 RS-232/V.24 标准的控制信号
  4. SEND_BREAK
  5. SERIAL_STATE  返回状态信息,比如:奇偶校验错误
  6. SEND_ENCAPSULATED_COMMAND
  7. GET_ENCAPSULATED_RESPONSE
  8. 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字节。

实现上面的操作之后,即可使用串口工具打开设备产生的串口了。接下来实现串口传输的模拟:

  1. 从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

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注