《USB技术应用与开发》第七讲:CDC串口设备案例
学习目标:如何通过 USB 接口模拟出一个外设,让计算机可以识别为一个串口设备。
一、基本概念
CDC 全称 Communication Device Class,译为通讯设备类,是 USB 协议中定义的一个子类,是USB 通讯设备类。
CDC 类定义了与通信相关设备的一个抽象集合,今天讲解的是:传统纯电话业务模型下的抽象控制模型。
插入:平时使用的 USB 转串的小模块,比如 CH340 ,在使用时都需要安装厂家提供的驱动,所以这种 USB 小模块并不属于 CDC 虚拟串口,真正的 CDC 虚拟串口一般的操作系统都会集成了驱动,所以可以有以下的简单判断:
厂家定义驱动一般都属于厂家自定义设备,这样的设备是由厂家提供的驱动程序生成串口,并由电脑显示出来,这样的方式比较灵活,但是驱动程序的开发比较复杂,兼容性困难。
二、描述符
2.1设备描述符
共 18 个字节,下面是与 CDC 相关需要改变的几个字段。
在之前的 HID 与大容量类设备的时候,以上三个字段都是 00h(接口,下图中的红色字段)。
以上的 02h 表示的就是:通讯设备类,依据来自于下图。
CDC 有 CDC 控制类(第一行黄色的)并在描述那一列写的是 Both ,表明在 设备(Device)和 接口(Interface)都要描述该 CDC 控制类。
CDC 还有 CDC 数据类(第二行黄色的)并在描述那一列定义为接口。
所以接下来的讲解的 CDC 中除了有一个控制接口还有一个数据接口,它们在 接口描述符 的 02,0A 都需要写,在 设备描述符 层面要表明隶属 02h DeviceClass,子类和协议写 00 就可以(如上图)。
2.2配置描述符
无特别说明。
2.3接口描述符
2.3.1控制类接口描述符
CDC 必须有一个控制类接口作为数据类接口的依附,控制类接口描述符 v5、v6、v7 这三个类别(或者说字段)如下图。
对上图的解释:
字段 | 数值 | 描述 |
v5 | 02h | 类:控制类接口 |
v6 | 02h | 子类:抽象控制模型 |
v7 | 01h | 协议:通用 AT 命令集 |
以上就可以表示是一个虚拟串口。在接口描述符下面是允许有端点的,一般在 CDC 控制类接口下面会有一个中断端点,用来报告一些状态,但经过抓包测试即使没有中断端点也没有特殊情况,所以这里的中断端点是可选的,并不是必须的。
2.3.2数据类接口描述符
CDC 主要应用为虚拟串口进行串口收发通讯,具体的收发端点就是在这个数据类端点下实现的,具体字段要求如下。
2.4特定类描述符------功能描述符
HID 设备会在接口描述符后面出现 HID 类设备描述,对于 CDC 类设备在接口后面出现的特定描述符称之为功能描述符 。
功能描述符值作用是描述针对接口的功能,接口后面可以有多个功能描述符描述接口的功能。
后续的参数均与第三个“描述符子类型”有关,在具体的代码中讲解。
2.5端点描述符
CDC 数据接口有一组跟 U 盘一样的端点:一个 block in(批量上传端点),一个block out(批量下传端点),采用的是批量传输,使用的是 USB 协议标准的端点描述符,下图为列出的通用端点描述符,没有针对 CDC 来写。
三、类请求
下面是虚拟串口中比较常见的几个类请求,就是主机发送给设备的请求。
3.1获取串口数据格式&设置串口数据格式
格式包括但不限于:波特率、数据位、校验位、停止位等参数。
CDC 串口设备本质上模拟一个“串口”,但是由于串口需要设置以上等格式参数,所以主机也必须获取/设置 CDC 设备这些参数。
3.1.1获取串口数据
字段名 | 值(GetLineCoding) | 含义说明 |
---|---|---|
bmRequestType | 1010 0001 B (0xA1) | 类请求 + 接收方向 + 接口为目标 |
bRequest | GET_LINE_CODING (0x21) | 请求码,表示“获取当前串口配置” |
wValue | 0x0000 | 无含义,固定为 0 |
wIndex | Interface | 接口号,即 CDC 接口编号 |
wLength | 7 字节(结构大小) | 主机会从设备中读取这 7 字节 |
Data | Line Coding Structure | 结构体(7字节)用于返回串口参数 |
3.1.2设置串口数据
字段名 | 值(SetLineCoding) | 含义说明 |
---|---|---|
bmRequestType | 0010 0001 B (0x21) | 类请求 + 发送方向 + 接口为目标 |
bRequest | SET_LINE_CODING (0x20) | 请求码,表示“设置串口配置” |
wValue | 0x0000 | 无含义,固定为 0 |
wIndex | Interface | 接口号,即 CDC 接口编号 |
wLength | 7 字节(结构大小) | 主机会向设备发送这 7 字节数据 |
Data | Line Coding Structure | 结构体(7字节)描述串口新参数 |
3.1.3获取到/设置为 的7字节结构体


可以是5、6、7、8、16位数据(中文翻译版有误)
3.1.4举例
(1)主机获取串口数据
💻 主机向设备发送请求:
字段名 | 值 | 含义 |
---|---|---|
bmRequestType | 0xA1 | 1010 0001 → IN(设备→主机),Class类请求,Interface为目标 |
bRequest | 0x21 | GET_LINE_CODING 请求码 |
wValue | 0x0000 | 固定为 0 |
wIndex | 0x0000 | 接口编号,一般是 CDC 数据接口号(2.3.2节) |
wLength | 0x0007 | 一共读取 7 字节 |
Data | —— | 无,主机会接收 7 字节数据 |
📥 设备响应的数据(LineCoding 结构体):
比如当前串口配置是:波特率 115200、8 数据位、无校验、1停止位(8N1):
偏移 | 字段名 | 值 | 含义 |
---|---|---|---|
0 | dwDTERate | 0x00 0xC2 0x01 0x00 | 0x0001C200 → 115200(注意小端) |
4 | bCharFormat | 0x00 | 1 个停止位 |
5 | bParityType | 0x00 | 无校验 |
6 | bDataBits | 0x08 | 8 位数据 |
完整返回的 7 字节数据为:00 C2 01 00 00 00 08
(2)主机设置串口参数
现在主机想设置成:波特率 9600、7 数据位、偶校验、1.5 个停止位
💻 主机发送控制请求(含 7 字节数据):
字段名 | 值 | 含义 |
---|---|---|
bmRequestType | 0x21 | 0010 0001 → OUT(主机→设备),Class类请求,Interface为目标 |
bRequest | 0x20 | SET_LINE_CODING 请求码 |
wValue | 0x0000 | 固定为 0 |
wIndex | 0x0000 | 接口编号 |
wLength | 0x0007 | 向设备写入 7 字节 |
Data | 80 25 00 00 01 02 07 | 7 字节结构体内容(解析如下) |
📤 主机发送的数据解析:
偏移 | 字段名 | 值 | 含义 |
---|---|---|---|
0 | dwDTERate | 0x80 0x25 0x00 0x00 | 0x00002580 → 9600 bps |
4 | bCharFormat | 0x01 | 1.5 个停止位 |
5 | bParityType | 0x02 | 偶校验 |
6 | bDataBits | 0x07 | 7 位数据位 |
3.2设置控制线状态
驱动 RTS 和DTR 这两根线。
上图展示了 USB CDC 类中的类请求:设置(模拟)串口通信中控制信号线,比如RTS(Request to Send) 和 DTR(Data Terminal Ready)。
🧩 这条请求的完整结构解释如下:
字段名 | 含义 |
---|---|
bmRequestType | 00100001B ,表示类请求(Class)、主机到设备(Host to Device)、接口(Interface) |
bRequest | 0x22 ,即 SET_CONTROL_LINE_STATE |
wValue | 控制信号位图(Control Signal Bitmap)(下面会解释) |
wIndex | 接口号(一般为 CDC 接口号) |
wLength | 恒为 0,无数据阶段 |
Data | 无 |
🧠 wValue
(控制信号位图)详解:wValue
是一个 16 位 数,其中
Bit位 | 含义 | 对应串口信号 |
---|---|---|
D0 | DTR(Data Terminal Ready) | V.24 的信号 108/2 |
D1 | RTS(Request To Send) | V.24 的信号 105 |
D2~D15 | 保留,必须设为 0 | 无作用 |
(1)D0:DTR(Data Terminal Ready)
-
用来表示主机(DTE)是否已经“准备好”开始通信。
-
在传统串口中,这个信号用于通知设备(DCE)主机上线了。
-
0
= 未准备好
1
= 准备好通信(常用于“开机、连上电”的意义)
(2)D1:RTS(Request To Send)
-
用来请求设备允许发送数据。
-
在 半双工模式 下有效,用于协商谁发送谁接收。
-
0
= 停止请求发送(关闭发送权限)
1
= 请求发送数据(激活 carrier) -
⚠️ 设备一般在全双工模式(默认)中 会忽略 RTS。
-
如果主机设置 RTS=1,设备可能会根据这个信号开关接收缓冲区,或发送 ACK/NACK 等。
虽然没有真实的 RS-232 电平线,但USB 协议中会模拟这些控制线的功能,就通过我们之前学的 SetControlLineState (0x22)
请求实现:
-
设置 DTR = 1:表示主机“打开串口调试助手”
-
设置 RTS = 1:表示主机“点击发送前的准备动作”
3.3获取串口状态
感兴趣的自行了解。
四、软件实现及效果演示
4.1硬件平台
可以做全速的 CDC ,比特率最快应该就是 1 兆。
4.2软件框架
4.3代码
5.8必须更新完。
希望身边讨厌的人没有关注我的博客。
本专栏说明:本人是USB的初学者,该专栏是我CSDN上第一个学习USB技术的专栏,笔记会比较口语,不是很精炼,后续有点基础后,争取“字字珠玑”。
本文参考:
《USB技术应用与开发》第七讲:CDC串口设备案例_哔哩哔哩_bilibili