第 3 章 - USBX 设备堆栈的功能组件

本章从功能角度介绍了高性能 USBX 嵌入式 USB 设备堆栈。

执行概述

设备的 USBX 由多个组件组成。

  • 初始化
  • 应用程序接口调用
  • USB 设备类
  • USB 设备堆栈
  • 设备控制器
  • VBUS 管理器

下图展示了 USBX 设备堆栈。

USBX 设备堆栈

初始化

若要激活 USBX,必须调用函数 ux_system_initialize。 此函数会初始化 USBX 的内存资源。

若要激活 USBX 设备功能,必须调用函数 ux_device_stack_initialize。 此函数反过来会对 USBX 设备堆栈使用的所有资源(如 ThreadX 线程、互斥和信号灯)进行初始化。

应该通过应用程序初始化来激活 USB 设备控制器和一个或多个 USB 类。 与 USB 主机端相反,设备端每次只能运行一个 USB 控制器驱动程序。 当类已注册到堆栈并且设备控制器初始化函数已被调用时,总线将处于活动状态,堆栈会响应总线重置和主机枚举命令。

应用程序接口调用

USBX 中有两个级别的 API。

  • USB 设备堆栈 API
  • USB 设备类 API

通常,USBX 应用程序不必调用任何 USB 设备堆栈 API。 大多数应用程序将只访问 USB 类 API。

USB 设备类

类 API 与每个 USB 类特别相关。 USB 类的大多数常见 API 都提供诸如打开/关闭设备以及读取设备或向设备写入等服务。

存储类

存储类负责应答特定于存储类的控制请求和处理存储类协议命令。 有关详细信息,请参阅第 5 章中的 USB 设备存储类。

CDC 类

CDC 类负责响应 CDC 类特定的控制请求,并提供通过数据管道与主机通信的方式。 现在支持以下功能:

  • CDC-ACM:作为串行设备与主机通信
  • CDC-ECM:作为以太网设备与主机通信,有关详细信息,请参阅第 5 章中的 USB 设备 CDC-ACM 类和 USB 设备 CDC-ECM 类。

HID 类

HID 类负责回答 HID 类特定的控件请求,并提供与特定于 HID 类的报表通信主机的方法。 有关详细信息,请参阅第 5 章中的 USB 设备 HID 类。

自定义类

对于高级开发人员,可以创建更多自定义类、响应自定义控制请求和处理数据管道上的自定义协议。 请注意,此类可能还需要在主机端进行特定的自定义。

USB 设备堆栈

设备堆栈 API 负责注册 USBX 设备组件(例如类和设备框架)。

设备框架

USB 设备端负责设备框架的定义。 设备框架分为三个类别,如以下部分所述。

设备框架的组件的定义

设备框架的每个组件的定义与设备和设备使用的资源的性质有关。 下面是主要类别。

  • 设备描述符
  • 配置描述符
  • 接口描述符
  • 终结点描述符

USBX 支持高速和全速的设备组件定义(对低速的处理方式与全速相同)。 这允许设备在连接到高速或全速主机时以不同的方式运行。 典型区别在于每个终结点的大小和设备消耗的电量。

设备组件的定义采用遵循 USB 规范的字节字符串形式。 定义是连续的,框架在内存中的呈现顺序将与枚举期间返回到主机的顺序相同。

下面是高速 USB 闪存磁盘的设备框架示例。

#define DEVICE_FRAMEWORK_LENGTH_HIGH_SPEED 60
UCHAR device_framework_high_speed[] = {
    /* Device descriptor */
    0x12, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x0a, 0x07, 0x25, 0x40, 0x01, 0x00, 0x01, 0x02, 0x03, 0x01,

    /* Device qualifier descriptor */
    0x0a, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00,

    /* Configuration descriptor */
    0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0xc0, 0x32,

    /* Interface descriptor */
    0x09, 0x04, 0x00, 0x00, 0x02, 0x08, 0x06, 0x50, 0x00,

    /* Endpoint descriptor (Bulk Out) */
    0x07, 0x05, 0x01, 0x02, 0x00, 0x02, 0x00,

    /* Endpoint descriptor (Bulk In) */
    0x07, 0x05, 0x82, 0x02, 0x00, 0x02, 0x00
};

设备框架的字符串的定义

字符串在设备中是可选的。 它们的用途是通过 Unicode 字符串让 USB 主机知道设备的制造商、产品名称和修订号。

主字符串是设备描述符中嵌入的索引。 其他字符串索引可以嵌入到各个接口中。

假设上面的设备框架在设备描述符中嵌入了三个字符串索引,则字符串框架定义可能类似于下面的示例代码。

/* String Device Framework:
    Byte 0 and 1: Word containing the language ID: 0x0904 for US
    Byte 2 : Byte containing the index of the descriptor
    Byte 3 : Byte containing the length of the descriptor string
*/

#define STRING_FRAMEWORK_LENGTH 38
UCHAR string_framework[] = {
    /* Manufacturer string descriptor: Index 1 */
    0x09, 0x04, 0x01, 0x0c,
    0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x20, 0x4c,
    0x6f, 0x67, 0x69, 0x63,

    /* Product string descriptor: Index 2 */
    0x09, 0x04, 0x02, 0x0c,
    0x4D, 0x4C, 0x36, 0x39, 0x36, 0x35, 0x30, 0x30,
    0x20, 0x53, 0x44, 0x4B,

    /* Serial Number string descriptor: Index 3 */
    0x09, 0x04, 0x03, 0x04,
    0x30, 0x30, 0x30, 0x31
};

如果必须为每种速度使用不同的字符串,则必须使用不同的索引,因为索引与速度无关。

字符串的编码基于 UNICODE。 有关 UNICODE 编码标准的详细信息,请参阅以下出版物:

Unicode 标准,全球字符编码版本 1 第 1 卷和第 2 卷,Unicode 联合会,Addison-Wesley Publishing Company,Reading MA。

设备针对每个字符串支持的语言的定义

USBX 能够支持多种语言,但英语是默认语言。 字符串描述符的每种语言的定义采用语言定义数组形式,定义如下。

#define LANGUAGE_ID_FRAMEWORK_LENGTH 2
UCHAR language_id_framework[] = {
    /* English. */
    0x09, 0x04
};

若要支持更多语言,只需在默认英语代码之后添加语言代码双字节定义。 Microsoft 已在文档中定义了语言代码。

针对 Windows 95 和 Windows NT 开发国际软件:Nadine Kano,Microsoft Press,Redmond WA

设备控制器

设备控制器驱动程序 (DCD) 将 USB 设备堆栈操作与硬件操作互操作。 通常,USBX 应用程序不必调用设备控制器 API,初始化函数除外。 调用设备控制器初始化函数时,总线处于活动状态,堆栈将通过设备控制器驱动程序回复总线重置和主机枚举命令。

下面是 USB 设备堆栈可以运行的一些可能硬件:

  • STMicroelectronics 芯片与 USB 设备控制器
  • 带 USB 设备控制器的微芯片
  • 带 USB 设备控制器的 NXP 芯片
  • 带 USB 设备控制器的 Reneses 芯片
  • 其他带 USB 设备控制器的芯片等。

VBUS 管理器

在大多数 USB 设备设计中,VBUS 不属于 USB 设备核心,而是连接到用于监视线路信号的外部 GPIO。

因此,VBUS 必须与设备控制器驱动程序分开管理。

由应用程序向设备控制器提供 VBUS IO 的地址。 在设备控制器初始化之前,必须对 VBUS 进行初始化。

根据监视 VBUS 的平台规范,可以在 VBUS IO 初始化后让控制器驱动程序处理 VBUS 信号。如果这不可能,则应用程序必须提供用于处理 VBUS 的代码。

如果应用程序希望自行处理 VBUS,则它唯一的要求是在检测到设备已被提取时调用函数 ux_device_stack_disconnect。 在插入设备时无需通知控制器,因为当检测到总线复位断言/解除断言信号时,控制器会被唤醒。