usb

USB 2.0

USB2.0协议是一种高速串行的通信协议,它采用4线系统,包括D+、D-差分信号线,VBUS和GND地线。

USB2.0协议支持低速(Low Speed)、全速(Full Speed)和高速(High Speed)三种不同的数据传输模式。以下是这三种模式的详细解释:

  1. 低速模式(Low Speed)

    • 传输速率:最高可达1.5Mbps。
    • 应用场景:主要用于低速设备,如键盘、鼠标等输入设备。
    • 特点:功耗低,对数据传输速度要求不高的设备常采用此模式。
  2. 全速模式(Full Speed)

    • 传输速率:最高可达12Mbps。
    • 应用场景:适用于大多数USB 1.1时代的设备,如早期的U盘、打印机等。
    • 特点:相比于低速模式,全速模式提供了更快的数据传输速度,但仍然适用于那些不需要极高传输速度的设备。
  3. 高速模式(High Speed)

    • 传输速率:最高可达480Mbps。
    • 应用场景:适用于需要高速数据传输的设备,如高清摄像头、外部硬盘等。
    • 特点:高速模式极大地提高了数据传输速度,使得USB 2.0能够满足现代设备对高速数据传输的需求。

需要注意的是,虽然USB 2.0协议支持这三种传输模式,但具体设备可能只支持其中的一种或几种模式。此外,USB 2.0还具有向下兼容的特性,即USB 2.0接口可以支持USB 1.1和USB 1.0的设备。

标准请求

标准USB请求(Standard USB Requests)是USB协议中定义的一组通用请求,用于与USB设备通信并控制其操作。这些请求通常由主机发送到设备,设备根据请求的类型和参数执行相应的操作。如下所示:

OffsetFieldSizeValueDescription
0bmRequestType1请求类型,包括方向(D7)、类型(D6-D5)和接收者(D4-D0)。
1bRequest1请求代码,标识具体要执行的操作。
2wValue2请求的特定值或参数,其意义取决于请求代码。
4wIndex2索引或选择符,用于标识特定的接口、端点或其他资源。
6wLength2期望从设备接收的数据长度(对于IN方向请求)或发送到设备的数据长度(对于OUT方向请求)。

设备描述符

USB设备描述符是USB协议中用于描述USB设备特性和属性的数据结构。如下表所示:

OffsetFieldSizeValueDescription
0bLength10x12描述符的长度,以字节为单位
1bDescriptorType11描述符的类型,1表示设备描述符
2bcdUSB20x0200USB版本号,此处表示USB 2.0
4bDeviceClass10x00设备类别,0x00表示该字段由接口描述符定义
5bDeviceSubClass10x00设备子类,0x00表示该字段由接口描述符定义
6bDeviceProtocol10x00设备协议,0x00表示该字段由接口描述符定义
7bMaxPacketSize0164端点0的最大包大小,以字节为单位
8idVendor2XXXX厂商ID,由厂商指定
10idProduct2YYYY产品ID,由厂商指定
12bcdDevice2ZZZZ设备版本号,由厂商定义
14iManufacturer1厂商字符串描述符的索引值
15iProduct1产品字符串描述符的索引值
16iSerialNumber1序列号字符串描述符的索引值,可选
17bNumConfigurations1支持的配置描述符数量

配置描述符

USB配置描述符用于描述USB设备的配置信息,它包含了设备的配置集合,每个配置可能包括一个或多个接口。如下表所示:

OffsetFieldSizeValueDescription
0bLength10x09描述符的长度,以字节为单位。对于配置描述符,通常是9字节(基本配置描述符)加上每个接口描述符的长度。
1bDescriptorType10x02描述符类型,对于配置描述符,值为2。
2wTotalLength2配置描述符的总长度,包括所有接口描述符、端点描述符等附加描述符的长度。
4bNumInterfaces1此配置支持的接口数量。一个USB设备可以有一个或多个接口,每个接口对应一种设备功能。
5bConfigurationValue1选择此配置的索引值。当主机选择配置时,会使用这个值。
6iConfiguration1描述此配置的字符串描述符的索引。这个字符串描述符通常包含有关此配置的描述性信息。
7bmAttributes1配置的特性,如是否支持远程唤醒等。
8MaxPower1设备从总线获取的最大电流,以2mA为单位。例如,值50表示设备最大可以从总线获取100mA的电流。

接口描述符

接口描述符提供了关于USB设备中特定接口的信息,每个接口代表设备的一种功能或特性。如下表所示:

OffsetFieldSizeValueDescription
0bLength19描述符的长度,以字节为单位,对于接口描述符通常是9字节。
1bDescriptorType14描述符类型,对于接口描述符,值为4。
2bInterfaceNumber1接口的编号,用于区分设备的不同接口。
3bAlternateSetting1备用设置的编号。当接口有多个配置时,可以使用不同的备用设置。
4bNumEndpoints1该接口使用的端点数量(不包括端点0)。
5bInterfaceClass1接口的类别代码,用于标识接口的类型或功能。
6bInterfaceSubClass1接口的子类别代码,进一步细分接口的类型或功能。
7bInterfaceProtocol1接口的协议代码,定义接口使用的通信协议或方法。
8iInterface1描述该接口的字符串描述符的索引。这个字符串通常包含接口的描述性信息。

端点描述符

端点描述符提供了关于USB设备中特定端点的信息,端点是数据传输的通道。以下是一个端点描述符的表格展示,按照Offset(偏移量)、Field(字段名)、Size(大小)和Description(描述)进行排列:

OffsetFieldSizeValueDescription
0bLength17描述符的长度,以字节为单位,对于端点描述符通常是7字节。
1bDescriptorType15描述符类型,对于端点描述符,值为5。
2bEndpointAddress1端点的地址,包括方向和编号。D7表示方向(0为OUT,1为IN),D6-D0为端点编号。
3bmAttributes1端点的属性,如传输类型(控制、中断、批量或同步)。
4wMaxPacketSize20x0040/0x0100端点可以处理的最大数据包大小(以字节为单位)。
6bInterval1对于中断类型的端点,这是轮询间隔。对于其他类型的端点,此值通常被忽略。

CDC

vcp

设备描述符,将设备配置为CDC设备。
```c
unsigned char DeviceDescriptor[18] =
{
    0x12, 0x01,
    0x00, 0x02,   // USB2.0
    0x02,       // CDC
    0x02,       //
    0x02,       //

    0x40,       //bMaxPacketSize0

    0x00, 0x00, //idVendor
    0x00, 0x00, //idProduct
    0x00, 0x02, //bcdDevice

    0x01,       //iManufacturer
    0x02,       //iProduct
    0x03,       //iSerialNumber
    0x01        //bNumConfigurations
};

关于配置描述符:

  • 端点3主要用于控制信号
  • 端点1和端点2用于数据传输[TXD/RXD]
#define CONFIG_DESC_SIZE    (9 + 8 + 35 + 23)      //VCP
static unsigned char ConfigurationDescriptor[CONFIG_DESC_SIZE] =
{
    // config
    0x09, 0x02,
    sizeof(ConfigurationDescriptor) & 0xFF,
    (sizeof(ConfigurationDescriptor) >> 8) & 0xFF,
    0x02,
    0x01,
    0x00,
    0x80,
    0x32,

    /*---------------------------------------------------------------------------*/
    // IAD 接口关联描述符
    0x08,        //描述符大小
    0x0B,        //IAD描述符类型
    0x00,        // bFirstInterface
    0x02,        // bInterfaceCount
    0x02,        // bFunctionClass: CDC Class
    0x02,        // bFunctionSubClass
    0x01,        // bFunctionProtocol
    0x00,        // iFunction

    // ------------- CDC --------------
    // Interface Descriptor
    0x09, 0x04,
    0x00,
    0x00,
    0x01, // In
    0x02, // Communication Interface Class
    0x02, // Abstract Control Model
    0x01, // Common AT commands
    0x00,

    /*Header Functional Descriptor*/
    0x05,   /* bLength: Endpoint Descriptor size */
    0x24,   /* bDescriptorType: CS_INTERFACE */
    0x00,   /* bDescriptorSubtype: Header Func Desc */
    0x10,   /* bcdCDC: spec release number */
    0x01,
    /*Call Management Functional Descriptor*/
    0x05,   /* bFunctionLength */
    0x24,   /* bDescriptorType: CS_INTERFACE */
    0x01,   /* bDescriptorSubtype: Call Management Func Desc */
    0x00,   /* bmCapabilities: D0+D1 */
    0x01,   /* bDataInterface: 1 */
    /*ACM Functional Descriptor*/
    0x04,   /* bFunctionLength */
    0x24,   /* bDescriptorType: CS_INTERFACE */
    0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
    0x02,   /* bmCapabilities */
    /*Union Functional Descriptor*/
    0x05,   /* bFunctionLength */
    0x24,   /* bDescriptorType: CS_INTERFACE */
    0x06,   /* bDescriptorSubtype: Union func desc */
    0x00,   /* bMasterInterface: Communication class interface */
    0x01,   /* bSlaveInterface0: Data Class Interface */

    // in_endpoint
    0x07, 0x05,
    0x83,        // bEndpointAddress: (IN3)
    0x03,        // Interrupt
    0x40, 0x00,  //wMaxPacketSize
    0x02,        //bInterval

    /*Data class interface descriptor*/
    0x09, 0x04,
    0x01,   /* bInterfaceNumber: Number of Interface */
    0x00,   /* bAlternateSetting: Alternate setting */
    0x02,   /* bNumEndpoints: Two endpoints used */
    0x0A,   /* bInterfaceClass: CDC */
    0x00,   /* bInterfaceSubClass: */
    0x00,   /* bInterfaceProtocol: */
    0x00,   /* iInterface: */

    0x07, 0x05,
    0x02,   /* bEndpointAddress: (OUT2) */
    0x02,   /* bmAttributes: Bulk */
    0x40, 0x00,
    0x00,   /* bInterval: ignore for Bulk transfer */

    0x07, 0x05,
    0x81,   /* bEndpointAddress: (IN1) */
    0x02,   /* bmAttributes: Bulk */
    0x40, 0x00,
    0x00,    /* bInterval */
};

端点0中端配置,多添加三条指令。

指令说明
Get Line Coding读取串口配置
Set Line Coding设置串口配置
Set Control Line State设置串口控制信号(如DTR/RTS)

配置格式: 波特率[4B] + 停止位[1B] + 校验位[1B] + 数据位[1B]

// --------------------------------------------------------
//                      VCP
// --------------------------------------------------------
else if((setup_ep0[0] == 0xA1) && (setup_ep0[1] == 0x21))
{
   // Get Line Coding

   // 默认配置波特率为115200, 数据位8, 停止位1, 无校验
   uint8_t vcp_attr[7] = {0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08};

   // 读取串口配置,如果该函数没读取串口信息,则先用默认配置。
   vcp_get_config(vcp_attr);

   ep0_send(vcp_attr, 7);
}
else if((setup_ep0[0] == 0x21) && (setup_ep0[1] == 0x20))
{
   // Set Line Coding
   // 接收主机发送的线编码信息
   usb_ep0_recv(buff, 64);

   // 根据配置格式解析,并更新UART配置
   vcp_set_config(buff);

   usb_vcp = true;
}
else if((setup_ep0[0] == 0x21) && (setup_ep0[1] == 0x22))
{
   // Set Control Line State

   // 控制DTR/RTS等信号,可以根据实际需求来处理。
   // ep0_send_empty();
}
else if((setup_ep0[0] == 0x21) && (setup_ep0[1] == 0x0a))
{
   usb_ep0_recv(buff, 64);
}

通讯伪代码:

void vcp_uart_handler(void)
{
#if 0
    uart_send(uart_vcp, "*", 1);
    if (uart_get_st(uart_vcp, UART_SR_RNE))
    {
        usb_sbuf[usb_send_idx] = uart_vcp->DR;
        usb_send_idx++;
        
        uart_set_overtime(uart_vcp, 20);
        uart_overtime_enable(uart_vcp, true);
    }
    else if(uart_get_st(uart_vcp, UART_SR_TIMEROUT))
    {
        uart_overtime_enable(uart_vcp, false);
        uart_clr_st(uart_vcp, UART_SR_TIMEROUT);
        
        usb_slen = usb_send_idx;
        usb_send_idx = 0;
        
        uart_recv_complete = true;
    }
#else
    if (uart_get_st(uart_vcp, UART_SR_RNE))
    {
        LED2_ON();
        
        uart_dma_recv(uart_vcp, uart_usb_rbuf, UART_MAX_SIZE, dmax, 0);
        
        uart_set_overtime(uart_vcp, 20);
        uart_overtime_enable(uart_vcp, true);
    }
    else if(uart_get_st(uart_vcp, UART_SR_TIMEROUT))
    {
        uart_clr_st(uart_vcp, UART_SR_TIMEROUT);
        uart_overtime_enable(uart_vcp, false);
        
        uart_len = dma_valid_len(dmax, 0, UART_MAX_SIZE);
        dma_enable(dmax, 0, false);
        
        uart_recv_complete = true;
        
        LED2_OFF();
    }
#endif
}

void usb_vcp_demo(void)
{
    uint32_t length = 0;
    usb_app_init();
    
    usb_recv_start(EP_RECV);
    while(1)
    {
        if(usb_vcp == true)
        {
            if(usb_recv_flag(EP_RECV) == true)
            {
                LED1_ON();
                if(usb_recv_idx + 64 > VCP_BUF_SIZE_MAX)
                {
                    usb_recv_idx = 0;
                }
                length = usb_recv(EP_RECV, &usb_rbuf[usb_recv_idx]);
                
                uart_dma_send(uart_vcp, usb_rbuf + usb_recv_idx, length, dmax, 1);
                dma_wait(dmax, 1);
                
                usb_recv_idx += 64;
                LED1_OFF();
            }
            
            if(uart_recv_complete == true)
            {
                uart_recv_complete = false;
                
                usb_ep1_send(usb_sbuf, usb_slen);
            }
        }
    }
}

USB 3.0