usb
USB 2.0
USB2.0协议是一种高速串行的通信协议,它采用4线系统,包括D+、D-差分信号线,VBUS和GND地线。
USB2.0协议支持低速(Low Speed)、全速(Full Speed)和高速(High Speed)三种不同的数据传输模式。以下是这三种模式的详细解释:
低速模式(Low Speed):
- 传输速率:最高可达1.5Mbps。
- 应用场景:主要用于低速设备,如键盘、鼠标等输入设备。
- 特点:功耗低,对数据传输速度要求不高的设备常采用此模式。
全速模式(Full Speed):
- 传输速率:最高可达12Mbps。
- 应用场景:适用于大多数USB 1.1时代的设备,如早期的U盘、打印机等。
- 特点:相比于低速模式,全速模式提供了更快的数据传输速度,但仍然适用于那些不需要极高传输速度的设备。
高速模式(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设备通信并控制其操作。这些请求通常由主机发送到设备,设备根据请求的类型和参数执行相应的操作。如下所示:
| Offset | Field | Size | Value | Description |
|---|---|---|---|---|
| 0 | bmRequestType | 1 | 请求类型,包括方向(D7)、类型(D6-D5)和接收者(D4-D0)。 | |
| 1 | bRequest | 1 | 请求代码,标识具体要执行的操作。 | |
| 2 | wValue | 2 | 请求的特定值或参数,其意义取决于请求代码。 | |
| 4 | wIndex | 2 | 索引或选择符,用于标识特定的接口、端点或其他资源。 | |
| 6 | wLength | 2 | 期望从设备接收的数据长度(对于IN方向请求)或发送到设备的数据长度(对于OUT方向请求)。 |
设备描述符
USB设备描述符是USB协议中用于描述USB设备特性和属性的数据结构。如下表所示:
| Offset | Field | Size | Value | Description |
|---|---|---|---|---|
| 0 | bLength | 1 | 0x12 | 描述符的长度,以字节为单位 |
| 1 | bDescriptorType | 1 | 1 | 描述符的类型,1表示设备描述符 |
| 2 | bcdUSB | 2 | 0x0200 | USB版本号,此处表示USB 2.0 |
| 4 | bDeviceClass | 1 | 0x00 | 设备类别,0x00表示该字段由接口描述符定义 |
| 5 | bDeviceSubClass | 1 | 0x00 | 设备子类,0x00表示该字段由接口描述符定义 |
| 6 | bDeviceProtocol | 1 | 0x00 | 设备协议,0x00表示该字段由接口描述符定义 |
| 7 | bMaxPacketSize0 | 1 | 64 | 端点0的最大包大小,以字节为单位 |
| 8 | idVendor | 2 | XXXX | 厂商ID,由厂商指定 |
| 10 | idProduct | 2 | YYYY | 产品ID,由厂商指定 |
| 12 | bcdDevice | 2 | ZZZZ | 设备版本号,由厂商定义 |
| 14 | iManufacturer | 1 | 厂商字符串描述符的索引值 | |
| 15 | iProduct | 1 | 产品字符串描述符的索引值 | |
| 16 | iSerialNumber | 1 | 序列号字符串描述符的索引值,可选 | |
| 17 | bNumConfigurations | 1 | 支持的配置描述符数量 |
配置描述符
USB配置描述符用于描述USB设备的配置信息,它包含了设备的配置集合,每个配置可能包括一个或多个接口。如下表所示:
| Offset | Field | Size | Value | Description |
|---|---|---|---|---|
| 0 | bLength | 1 | 0x09 | 描述符的长度,以字节为单位。对于配置描述符,通常是9字节(基本配置描述符)加上每个接口描述符的长度。 |
| 1 | bDescriptorType | 1 | 0x02 | 描述符类型,对于配置描述符,值为2。 |
| 2 | wTotalLength | 2 | 配置描述符的总长度,包括所有接口描述符、端点描述符等附加描述符的长度。 | |
| 4 | bNumInterfaces | 1 | 此配置支持的接口数量。一个USB设备可以有一个或多个接口,每个接口对应一种设备功能。 | |
| 5 | bConfigurationValue | 1 | 选择此配置的索引值。当主机选择配置时,会使用这个值。 | |
| 6 | iConfiguration | 1 | 描述此配置的字符串描述符的索引。这个字符串描述符通常包含有关此配置的描述性信息。 | |
| 7 | bmAttributes | 1 | 配置的特性,如是否支持远程唤醒等。 | |
| 8 | MaxPower | 1 | 设备从总线获取的最大电流,以2mA为单位。例如,值50表示设备最大可以从总线获取100mA的电流。 |
接口描述符
接口描述符提供了关于USB设备中特定接口的信息,每个接口代表设备的一种功能或特性。如下表所示:
| Offset | Field | Size | Value | Description |
|---|---|---|---|---|
| 0 | bLength | 1 | 9 | 描述符的长度,以字节为单位,对于接口描述符通常是9字节。 |
| 1 | bDescriptorType | 1 | 4 | 描述符类型,对于接口描述符,值为4。 |
| 2 | bInterfaceNumber | 1 | 接口的编号,用于区分设备的不同接口。 | |
| 3 | bAlternateSetting | 1 | 备用设置的编号。当接口有多个配置时,可以使用不同的备用设置。 | |
| 4 | bNumEndpoints | 1 | 该接口使用的端点数量(不包括端点0)。 | |
| 5 | bInterfaceClass | 1 | 接口的类别代码,用于标识接口的类型或功能。 | |
| 6 | bInterfaceSubClass | 1 | 接口的子类别代码,进一步细分接口的类型或功能。 | |
| 7 | bInterfaceProtocol | 1 | 接口的协议代码,定义接口使用的通信协议或方法。 | |
| 8 | iInterface | 1 | 描述该接口的字符串描述符的索引。这个字符串通常包含接口的描述性信息。 |
端点描述符
端点描述符提供了关于USB设备中特定端点的信息,端点是数据传输的通道。以下是一个端点描述符的表格展示,按照Offset(偏移量)、Field(字段名)、Size(大小)和Description(描述)进行排列:
| Offset | Field | Size | Value | Description |
|---|---|---|---|---|
| 0 | bLength | 1 | 7 | 描述符的长度,以字节为单位,对于端点描述符通常是7字节。 |
| 1 | bDescriptorType | 1 | 5 | 描述符类型,对于端点描述符,值为5。 |
| 2 | bEndpointAddress | 1 | 端点的地址,包括方向和编号。D7表示方向(0为OUT,1为IN),D6-D0为端点编号。 | |
| 3 | bmAttributes | 1 | 端点的属性,如传输类型(控制、中断、批量或同步)。 | |
| 4 | wMaxPacketSize | 2 | 0x0040/0x0100 | 端点可以处理的最大数据包大小(以字节为单位)。 |
| 6 | bInterval | 1 | 对于中断类型的端点,这是轮询间隔。对于其他类型的端点,此值通常被忽略。 |
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);
}
}
}
}
