USB深度解析(三):库调用讲解

USB深度解析(三):库调用讲解

在前两篇文章中,我们从底层电气信号、包/事务/传输模式,再到USB设备枚举完整流程、标准请求、全套描述符完成理论讲解。理论中主机发送SETUP请求、读取各类描述符、分配地址、激活配置,所有设备侧应答逻辑,在STM32的USB Device库中全部由软件代码实现。本篇以CDC虚拟串口为例,拆解STM32 USB设备库各源文件职责,把上一篇的枚举流程和C代码一一对应,理清「主机标准请求→库底层处理→应用层回调→返回描述符/应答」完整链路。

一、STM32 USB设备库整体分层对照

把USB协议逻辑映射到软件分层,和前文协议模型做对应:

USB协议层 协议内容 STM32库对应模块
物理层 总线复位、SOF、包收发、NRZI、位填充、硬件端点寄存器 USB外设HAL驱动 + usbd_conf.c硬件适配
设备内核层 控制传输处理、SETUP请求解析、设备状态机、事务调度 usbd_core.c、usbd_ctlreq.c、usbd_ioreq.c
类驱动层 CDC类专属描述符、类请求、CDC接口/端点管理 usbd_cdc.c
用户应用层 设备描述符/字符串、数据收发回调、硬件参数配置 usbd_desc.c、usbd_cdc_if.c、usb_device.c、usbd_conf.c

二、GET_DESCRIPTOR 请求配置描述符集合

以下具体描述了主机请求配置描述符的过程中,设备端代码调用的过程:

  1. 主机请求配置描述符
  2. 硬件触发 HAL_PCD_SetupStageCallback 回调函数,调用 usbd_core 中的 USBD_LL_SetupStage,解析后判断为标准设备请求,调用 usbd_ctlreq 中的 USBD_StdDevReq 函数。
  3. usbd_ctlreq 识别请求类型为获取配置描述符,此时不再调用 usbd_desc.c;而是通过已注册的类驱动函数表,调用 usbd_cdc.c 中的 USBD_CDC_GetCfgDesc 回调获取描述符数据。
  4. 第一次请求 wLength=9:库内核通过 usbd_ioreq 封装的底层发送函数,将 usbd_cdc 提供的 9 字节配置描述符头发给主机。
  5. 主机解析得到 wTotalLength=0x43(67字节)
  6. 主机请求完整的配置描述符集合。
  7. 经过同样的硬件触发与回调流程,库内核再次通过 usbd_ioreq 将 usbd_cdc 提供的完整 67 字节配置集合一次性传给主机。

协议规则:主机第一次 wLength=9,获取配置头,读出 wTotalLength;第二次 wLength 等于 wTotalLength 读取全部配置集合,集合包含:配置描述符、接口描述符、类特定描述符、端点描述符。

usbd_desc.c:设备描述符 + 字符串描述符(类型0x01、0x03)

usbd_cdc.c:配置、接口、端点、CDC类特定描述符(类型0x02、0x04、0x05、CDC类24开头) 这就解释了为什么第二篇协议中,配置描述符集合是绑定在类驱动内部,不属于设备描述符。HID设备同理,HID的配置集合放在usbd_hid.c。

三、关键库文件深度解析

3.1 usbd_core.c:设备状态机与回调调度中心

核心职责:

  1. 维护USB设备状态:DEFAULT → ADDRESS → CONFIGURED,完全对应枚举状态流转;
  2. 管理USBD_HandleTypeDef设备句柄,句柄里面保存:设备状态、指向描述符回调(usbd_desc)、指向类驱动操作表(USBD_ClassTypeDef,CDC/HID/MSC);
  3. 总线事件入口:复位、SOF、挂起、恢复事件全部从硬件中断上报到usbd_core;
  4. 分发请求:当收到标准请求,把请求转发给类驱动 或者描述符回调

3.2 usbd_ctlreq.c:标准请求解析器

该文件专门解析SETUP包8字节:bmRequestType、bRequest、wValue、wIndex、wLength,实现第二篇表格中11种USB标准请求。

所有SETUP包的语义解析全部在这里,相当于协议的"译码器",是协议与软件之间最重要桥梁。

3.3 usbd_ioreq.c:控制传输底层事务封装

封装端点0的IN/OUT事务,处理DATA0/DATA1 toggle切换、ZLP零长度包,处理数据分段传输(当描述符大于端点0最大包长时分包发送)。 当usbd_ctlreq解析完请求之后,真正发起数据包收发,交给usbd_ioreq

3.4 usbd_cdc.c(库类驱动文件)

  1. 存放完整CDC配置描述符集合(配置、接口、CDC类特定描述符、端点描述符);
  2. 实现USBD_CDC_GetCfgDesc(),内核GET_DESCRIPTOR请求配置描述符时调用;
  3. 实现CDC类请求处理(除USB标准请求外,CDC ACM还有自己的类控制请求,如设置波特率、停止位等);
  4. 提供类驱动函数表:USBD_CDC,包含SetConfig、SetInterface等回调,注册进usbd_core;
  5. 管理CDC使用的端点:中断输入端点、批量IN/OUT端点;枚举SET_CONFIGURATION时打开这几个端点。

3.5 usbd_desc.c 用户层

存放设备描述符 + 字符串描述符(类型0x01、0x03)的具体内容和回调函数实现,实现一组回调函数,主机通过设备描述符中索引即可正常调用:

  • USBD_CDC_DeviceDescriptor:返回设备描述符(0x01)
  • USBD_CDC_LangIDStrDescriptor:语言ID字符串描述符(索引0)
  • USBD_CDC_ManufacturerStrDescriptor:厂商字符串
  • USBD_CDC_ProductStrDescriptor:产品字符串
  • USBD_CDC_SerialStrDescriptor:序列号字符串
  • USBD_CDC_ConfigStrDescriptor:配置字符串描述符
  • USBD_CDC_InterfaceStrDescriptor:接口字符串描述符

3.6 usb_device.c

应用层初始化入口,完成:

  1. 定义USBD_HandleTypeDef句柄;
  2. USBD_Init():注册usbd_desc;
  3. USBD_RegisterClass()注册CDC类驱动;
  4. 调用USBD_Start()启动USB外设。

3.7 usbd_cdc_if.c

枚举完成,设备状态变为CONFIGURED,批量端点可以收发数据之后,库内核收到CDC批量OUT数据,调用CDC_Receive_FS();用户在该回调里面拿到串口接收数据;用户发送数据调用USBD_CDC_Transmit_FS()

该文件不参与设备枚举流程

3.8 usbd_conf.c

硬件适配层,实现USB中断服务函数,把硬件中断事件上报usbd_core;配置端点缓冲区大小、硬件PHY参数,和寄存器打交道。

小结

STM32 USB设备库本质就是USB2.0设备协议的软件实现

  • 硬件与中断由usbd_conf.c承接;
  • usbd_core.c维护设备状态机,作为事件分发中枢;
  • usbd_ctlreq.c解析SETUP标准请求(对应第二篇1标准请求8字节SETUP包);
  • usbd_ioreq.c实现控制传输的事务分包、toggle管理;
  • 设备描述符、字符串描述符放在usbd_desc.c;配置/接口/端点/CDC类特定描述符全部放在类驱动usbd_cdc.c
  • 枚举完成之后业务数据流才交给usbd_cdc_if.c