USB CDC串口通信详解:把USB变成“智能串口线”

🔥作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生,研究方向无线联邦学习

🎬擅长领域:驱动开发,嵌入式软件开发,BSP开发

❄️作者主页:一个平凡而乐于分享的小比特的个人主页

✨收录专栏:通信协议,本专栏为记录项目中用到的知识点,以及一些硬件常识总结

欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖

USB CDC串口通信详解:把USB变成"智能串口线"

一、CDC是什么?------USB时代的串口革命

CDC(Communications Device Class,通信设备类) 是一种USB设备类规范,它让USB设备可以模拟传统的串口(COM口),但性能大幅提升。

传统串口 vs USB CDC串口

复制代码
传统串口(RS-232)时代:
┌─────────┐  串口线(9针)  ┌─────────┐
│ 单片机 │←---慢速+大电压--→│  电脑  │
└─────────┘ 最大115.2Kbps  └─────────┘
    问题:速度慢、需要专用接口、电压高

USB CDC时代:
┌─────────┐  USB CDC虚拟  ┌─────────┐
│ 嵌入式 │←---高速+即插即用--→│  电脑  │
└─────────┘ 可达12Mbps    └─────────┐
    优势:快、简单、标准、可供电

二、CDC在USB体系中的位置

复制代码
USB设备类体系:
┌─────────────────────────────────┐
│      USB标准设备类               │
├─────────────────────────────────┤
│ 1️⃣ 大容量存储类(U盘、移动硬盘)  │
│ 2️⃣ HID类(键盘、鼠标)           │
│ 3️⃣ **CDC类(串口、网卡、调制解调器)**│ ← 今天的主角
│ 4️⃣ 音频类(耳机、麦克风)         │
│ 5️⃣ 视频类(摄像头)              │
└─────────────────────────────────┘

CDC子类(重点看串口):
• CDC ACM(Abstract Control Model):虚拟串口
• CDC ECM(Ethernet Control Model):虚拟网卡
• CDC NCM(Network Control Model):网络聚合
• CDC PCC(Phone Control Model):电话控制

三、CDC-ACM虚拟串口架构

CDC-ACM设备内部结构图

复制代码
CDC-ACM设备内部端点配置:
┌─────────────────────────────────┐
│        CDC-ACM设备               │
│                                  │
│  控制接口(端点0 - 必需)         │ ← 管理命令
│   ├─ 类特定描述符                 │
│   └─ 端点0(双向控制端点)        │
│                                  │
│  数据接口(端点1、2 - 数据传输)   │
│   ├─ 端点1 IN(设备→主机)        │ ← 发送数据
│   └─ 端点2 OUT(主机→设备)       │ ← 接收数据
│                                  │
│  可选的端点3 IN(通知端点)       │ ← 状态变化通知
└─────────────────────────────────┘

四、CDC-ACM通信协议栈

复制代码
CDC-ACM通信层次:
应用层(用户程序)
    ↓
串口API(COM1, COM2...)
    ↓
USB CDC驱动程序(usbser.sys / cdc_acm.ko)
    ↓
USB核心驱动
    ↓
USB主机控制器
    ↓
物理USB连接

五、CDC-ACM描述符详解(设备的"身份证")

描述符集合结构

复制代码
标准USB描述符 + CDC类特定描述符:

1. 设备描述符:我是CDC设备
   ┌─────────────────────────┐
   │ bDeviceClass:    0x02   │ ← 通信设备类
   │ bDeviceSubClass: 0x00   │
   │ bDeviceProtocol: 0x00   │
   └─────────────────────────┘

2. 配置描述符:我有两个接口
   ┌─────────────────────────┐
   │ 接口0:通信控制接口      │ ← 管理串口参数
   │ 接口1:数据接口          │ ← 实际数据传输
   └─────────────────────────┘

3. CDC类特定描述符(关键!):
   ┌─────────────────────────────────┐
   │ 头部功能描述符                   │
   │  • bDescriptorType: 0x24        │
   │  • bDescriptorSubtype: 0x00     │
   ├─────────────────────────────────┤
   │ 呼叫管理功能描述符               │
   │  • bmCapabilities: 0x00         │
   │  • bDataInterface: 0x01         │ ← 指向数据接口
   ├─────────────────────────────────┤
   │ ACM功能描述符                   │
   │  • bmCapabilities: 0x02         │ ← 支持线路控制
   ├─────────────────────────────────┤
   │ 联合功能描述符                  │
   │  • bControlInterface: 0x00      │ ← 控制接口编号
   │  • bSubordinateInterface: 0x01  │ ← 数据接口编号
   └─────────────────────────────────┘

六、CDC-ACM控制命令(串口参数设置)

标准串口控制命令表

命令代码 功能 参数 对应传统串口操作
0x00 SET_LINE_CODING 波特率、数据位等 配置串口参数
0x01 GET_LINE_CODING 读取当前设置 读取串口配置
0x02 SET_CONTROL_LINE_STATE RTS/DTR控制 硬件流控
0x03 SEND_BREAK 发送Break信号 特殊通信信号
0x04-0x1F 保留 - -
0x20-0xFF 类特定命令 厂商自定义 扩展功能

关键命令详解:SET_LINE_CODING

复制代码
数据格式(7字节):
┌─────────┬─────────┬─────────┬─────────┬─────────┐
│ 波特率   │ 停止位  │ 校验位  │ 数据位  │ 特殊位  │
│ (4字节)  │ (1字节) │ (1字节) │ (1字节) │ (1字节) │
├─────────┼─────────┼─────────┼─────────┼─────────┤
│ 115200  │   1位    │  无校验  │  8位    │   0     │
│ 0x0001C200│  0x00   │  0x00   │ 0x08   │  0x00   │
└─────────┴─────────┴─────────┴─────────┴─────────┘

常用波特率对应值:
• 9600    → 0x00002580
• 115200  → 0x0001C200  
• 921600  → 0x000E1000
• 1000000 → 0x000F4240

七、完整通信流程示例

场景:STM32通过CDC虚拟串口发送"Hello"

复制代码
时间线分析:
┌─[设备插入]─────────────────────────────┐
│ 1. 主机枚举设备,识别为CDC-ACM         │
│ 2. 加载usbser.sys驱动                  │
│ 3. 创建COM3虚拟串口                    │
├─[用户打开串口助手]─────────────────────┤
│ 4. 串口助手打开COM3,设置115200-8-N-1  │
│ 5. 发送SET_LINE_CODING命令             │
│ 6. 发送SET_CONTROL_LINE_STATE(1,1)     │
├─[STM32发送数据]───────────────────────┤
│ 7. STM32准备数据 "Hello\r\n"           │
│ 8. 通过端点1 IN发送数据包              │
│    PID=IN, 地址=xx, 端点=0x81          │
│    DATA=[0x48,0x65,0x6C,0x6C,0x6F,0x0D,0x0A]│
│ 9. 主机回复ACK                         │
├─[用户发送数据]────────────────────────┤
│ 10. 用户输入"AT\r"                     │
│ 11. 主机通过端点2 OUT发送              │
│     DATA=[0x41,0x54,0x0D]              │
│ 12. STM32接收并回复ACK                 │
└───────────────────────────────────────┘

八、CDC-ACM数据端点工作机制

数据流示意图

复制代码
双向全双工通信:
┌─────────────┐               ┌─────────────┐
│   主机       │               │   设备       │
│ (Windows/Linux)│             │(STM32/ESP32)│
├─────────────┤               ├─────────────┤
│ 串口应用     │               │ 应用程序     │
│    ↓         │               │    ↓        │
│ CDC驱动      │               │ CDC固件     │
│    ↓         │               │    ↓        │
│ USB核心      │←---USB总线--→│ USB控制器   │
└─────────────┘               └─────────────┘

端点使用:
• 端点0:控制传输(配置命令)
• 端点1 IN:设备→主机(TX)
• 端点2 OUT:主机→设备(RX)
• 端点3 IN:通知(可选)

数据包大小优化

复制代码
USB全速模式(12Mbps):
• 最大包大小:64字节
• 实际有效数据:≤63字节(1字节状态)
• 建议:一次发送≤60字节,避免碎片

USB高速模式(480Mbps):
• 最大包大小:512字节
• 可批量传输大块数据

九、实际开发:嵌入式CDC实现

STM32 CDC固件示例(简化)

c 复制代码
// 1. USB设备描述符配置
const uint8_t CDC_DeviceDescriptor[] = {
  0x12,        // 描述符长度
  0x01,        // 设备描述符类型
  0x00, 0x02,  // USB 2.0
  0x02,        // 设备类:通信设备
  0x00,        // 设备子类
  0x00,        // 设备协议
  0x40,        // 端点0最大包大小
  // ... 厂商ID、产品ID等
};

// 2. CDC类特定描述符
const uint8_t CDC_ClassDescriptor[] = {
  // 头部功能描述符
  0x05,        // 长度
  0x24,        // 描述符类型:CS_INTERFACE
  0x00,        // 头部功能描述符子类型
  0x10, 0x01,  // USB CDC规范1.10
  
  // ACM功能描述符
  0x04,        // 长度
  0x24,        // CS_INTERFACE
  0x02,        // ACM功能描述符
  0x02,        // 能力:支持线路控制
  
  // 联合功能描述符
  0x05,        // 长度
  0x24,        // CS_INTERFACE
  0x06,        // 联合功能描述符
  0x00,        // 控制接口编号
  0x01,        // 数据接口编号
};

// 3. 处理控制命令
void CDC_ControlRequest(uint8_t cmd) {
  switch(cmd) {
    case 0x00: // SET_LINE_CODING
      // 读取波特率、数据位等参数
      USBD_CDC_SetLineCoding(&line_coding);
      break;
      
    case 0x01: // GET_LINE_CODING
      // 返回当前串口配置
      USBD_CDC_GetLineCoding(&line_coding);
      break;
      
    case 0x02: // SET_CONTROL_LINE_STATE
      // 设置RTS/DTR状态
      USBD_CDC_SetControlLineState();
      break;
  }
}

// 4. 数据发送函数
void CDC_Transmit_DATA(uint8_t* data, uint16_t length) {
  // 通过端点1 IN发送数据
  USBD_CDC_TransmitPacket(CDC_IN_EP, data, length);
}

十、CDC与其他通信方式对比

特性 CDC-ACM虚拟串口 传统UART串口 USB HID USB大容量存储
速度 12Mbps(全速) 115.2Kbps 1.5Mbps(低速) 480Mbps(高速)
驱动需求 系统自带 系统自带 系统自带 系统自带
开发复杂度 中等 简单 中等 复杂
实时性 好(有中断端点) 最好
典型延迟 1-10ms 可变 1-10ms 100ms+
适用场景 调试、配置、通信 板间通信 人机交互 文件传输

十一、常见CDC设备实例

1. CP2102/CH340 USB转串口芯片

复制代码
工作流程:
电脑 ←USB(CDC)→ CP2102 ←UART(串口)→ 单片机
           ↓                   ↓
      虚拟COM口            真实串口信号

特点:
• 成本低(几元钱)
• 驱动广泛
• 支持多种波特率

2. STM32内置USB CDC

复制代码
STM32F4系列:
┌─────────────────┐
│   STM32F407     │
│                 │
│ 应用程序        │
│    ↓            │
│ USB CDC固件库   │
│    ↓            │
│ USB OTG控制器  │←USB线→电脑
│    ↓            │
│ UART外设        │←可连接其他设备
└─────────────────┘
优势:节省外部芯片,成本更低

3. ESP32-S2/S3 USB CDC

复制代码
ESP32-S3双芯片模式:
┌─────────────────┐
│    ESP32-S3     │
│                 │
│ 主CPU:运行用户程序 │
│    ↓            │
│ USB CDC协议栈   │
│    ↓            │
│ USB外设        │←用于程序下载+调试
│    ↓            │
│ UART0          │←连接外部设备
└─────────────────┘

十二、CDC通信的性能优化技巧

优化建议表

问题 原因 解决方案
数据丢失 缓冲区溢出 增加端点缓冲区大小
延迟大 轮询间隔长 使用中断传输代替批量
吞吐量低 小包发送 合并数据,使用最大包大小
CPU占用高 频繁中断 使用DMA传输
兼容性问题 描述符不规范 严格按照CDC规范

高级特性:USB CDC双通道

复制代码
双虚拟串口设备(STM32示例):
端点分配:
• 端点0:控制端点
• 端点1 IN:CDC1 TX
• 端点2 OUT:CDC1 RX
• 端点3 IN:通知端点
• 端点4 IN:CDC2 TX
• 端点5 OUT:CDC2 RX

应用场景:
1. 调试日志输出(CDC1)
2. AT命令通信(CDC2)
3. 互不干扰,独立波特率

十三、调试与故障排查

常见问题及解决

现象 可能原因 排查步骤
设备无法识别 描述符错误 使用USB分析仪查看枚举过程
串口不显示 驱动问题 检查设备管理器,重新安装驱动
数据乱码 波特率不匹配 检查SET_LINE_CODING命令
发送数据丢失 缓冲区满 检查NAK响应,调整发送间隔
只能单向通信 端点配置错误 检查IN/OUT端点配置

USB分析仪看到的CDC通信

复制代码
典型CDC通信分析:
[时间]      [方向]  [PID]    [内容]
00:00.001  主机→设备 SETUP   GET_DESCRIPTOR(设备)
00:00.002  设备→主机 DATA0   设备描述符
00:00.003  主机→设备 ACK     确认
00:00.010  主机→设备 SETUP   SET_CONFIGURATION(1)
00:00.011  设备→主机 ACK     确认
00:00.100  主机→设备 SETUP   SET_LINE_CODING
00:00.101  主机→设备 OUT     波特率115200数据
00:00.102  设备→主机 ACK     确认
00:00.200  设备→主机 IN      请求数据
00:00.201  设备→主机 DATA0   "Hello World"
00:00.202  主机→设备 ACK     确认

十四、CDC协议的未来发展

新特性与趋势

  1. 高速CDC:USB 3.0+支持,速度达5Gbps
  2. 多功能复合设备:CDC+大容量存储+HID集成
  3. 无线CDC:通过蓝牙或Wi-Fi模拟CDC
  4. WebUSB CDC:浏览器直接访问USB CDC设备
  5. 安全增强:加密通信,防窃听

十五、实用项目示例:DIY USB-CDC数据记录仪

硬件组成

复制代码
┌─────────────┐  USB CDC  ┌─────────────┐
│ 传感器       │←---数据---→│ STM32F103   │
│ (温湿度/DHT11)│          │ (USB CDC设备)│
└─────────────┘           └──────┬──────┘
                                  │ USB线
                         ┌───────┴───────┐
                         │  电脑          │
                         │ 串口助手/Python│
                         └───────────────┘

固件关键代码

c 复制代码
// 数据采集并发送
void DataLogger_Task(void) {
  float temperature, humidity;
  
  // 1. 读取传感器
  DHT11_Read(&temperature, &humidity);
  
  // 2. 格式化数据
  char buffer[64];
  sprintf(buffer, "Temp:%.1fC, Hum:%.1f%%\r\n", 
          temperature, humidity);
  
  // 3. 通过CDC发送
  CDC_Transmit_DATA((uint8_t*)buffer, strlen(buffer));
  
  // 4. 延时
  HAL_Delay(1000); // 每秒发送一次
}

总结:CDC的核心价值

CDC-ACM成功的关键 在于它在USB的现代化优势串口的简单易用之间找到了完美平衡:

  1. 对用户透明:看起来、用起来都和传统串口一样
  2. 性能大幅提升:速度从Kbps提升到Mbps级别
  3. 即插即用:无需额外电源,自动安装驱动
  4. 标准化:所有操作系统都支持
  5. 灵活性:可软件配置波特率等参数

CDC本质上是一个翻译官

  • 对电脑说:"我是标准串口设备"
  • 对USB硬件说:"请以高速USB协议传输数据"
  • 在中间进行协议转换

这使得无数基于串口的传统设备(工业控制器、传感器、老式设备)能够平滑迁移到USB时代,而无需改变上位机软件。正是这种"承上启下"的设计,让CDC成为嵌入式开发中最重要、最常用的USB设备类之一。

下次使用Arduino、STM32或ESP32的USB功能时,你会知道,这简单的"串口"背后,是一套完整、精巧的USB CDC协议栈在工作!