第 2 章 - Azure RTOS USBX 主机堆栈安装

主机注意事项

计算机类型

嵌入式开发通常在 Windows PC 或 Unix 主机计算机上执行。 在对应用程序进行编译和链接并将其放置在主机上之后,应用程序将下载到目标硬件进行执行。

下载接口

通常,虽然并行接口、USB 接口和以太网接口变得越来越普遍,但目标下载通过 RS-232 串行接口进行。 有关可用选项,请参阅开发工具文档。

调试工具

通常通过与程序映像下载相同的链接进行调试。 存在多种调试器,包括在目标上运行的小型监视器程序、后台调试监视器 (BDM) 和在线仿真器 (ICE) 工具等。 ICE 工具提供最可靠的实际目标硬件调试。

所需的硬盘空间

USBX 的源代码以 ASCII 格式提供,并要求主计算机的硬盘具有约 500 KB 可用空间。

目标注意事项

USBX 要求处于主机模式的目标具有 24 KB 到 64 KB 只读内存 (ROM)。 所需的内存量取决于所使用的控制器类型和链接到 USBX 的 USB 类。 USBX 全局数据结构和内存池要求目标具有额外的 32 KB 随机存取内存 (RAM)。 还可以根据 USB 接口上预期连接的设备数和 USB 控制器的类型调整此内存池。 USBX 设备端需要大约 10 K 到 12 K ROM,具体取决于设备控制器的类型。 RAM 内存使用量取决于设备仿真的类的类型。

USBX 还依赖于 ThreadX 信号灯、互斥体和线程进行多线程保护、I/O 暂停和定期处理,以监视 USB 总线拓扑。

产品分发

可以从我们的公共源代码存储库获取 Azure RTOS USBX,网址为:https://github.com/azure-rtos/usbx/

下面列出了该存储库中的几个重要文件:

  • ux_api.h:此 C 头文件包含所有系统等式、数据结构和服务原型。
  • ux_port.h:此 C 头文件包含所有特定于开发工具的数据定义和结构。
  • ux.lib:这是 USBX C 库的二进制版本。 它随标准包一起分发。
  • demo_usbx.c:包含简单 USBX 演示的 C 文件

所有文件名均为小写。 此命名约定使你可以更轻松地将命令转换为 Unix 开发平台命令。

USBX 安装

可以通过将 GitHub 存储库克隆到本地计算机来安装 USBX。 下面是用于在 PC 上创建 USBX 存储库的克隆的典型语法:

    git clone https://github.com/azure-rtos/usbx

或者,也可以使用 GitHub 主页上的“下载”按钮来下载存储库的副本。

你还可以在联机存储库的首页上找到有关生成 USBX 库的说明。

以下一般说明适用于几乎所有安装:

  1. 使用之前在主机硬盘驱动器上安装 ThreadX 的同一目录。 所有 USBX 名称都是唯一的,不会干扰以前的 USBX 安装。
  2. 在 tx_application_define 的开头或附近添加对 ux_system_initialize 的调用。这是初始化 USBX 资源的位置。
  3. 添加对 ux_host_stack_initialize 的调用。
  4. 添加一个或多个调用,以初始化所需的 USBX。
  5. 添加一个或多个调用,以初始化系统中可用的主机控制器。
  6. 可能需要修改 tx_low_level_initialize.c 文件,以添加低级别硬件初始化并中断矢量路由。 此说明特定于硬件平台,本文不做讨论。
  7. 编译应用程序源代码并将其与 USBX 运行时库和 ThreadX 运行时库(如果要编译 USB 存储类和/或 USB 网络类,则可能还需要 FileX 和/或 Netx)、ux.a(或 ux.lib)以及 tx.a(或 tx.lib)链接起来。 生成的库可下载到目标并执行。

配置选项

有多个配置选项用于生成 USBX 库。 所有选项都位于 ux_user.h 中。

以下列表详细介绍了每个配置选项。

  • UX_PERIODIC_RATE:此值表示特定硬件平台每秒的时钟周期数。 默认值为 1000,表示每毫秒一个时钟周期。
  • UX_MAX_CLASS_DRIVER:此值是 USBX 可加载的最大类数。 此值表示类容器,而不是类的实例数。 例如,如果特定的 USBX 实现需要集线器类、打印机类和存储类,则无论属于这些类的设备数量为多少,UX_MAX_CLASS_DRIVER 值都可以设置为 3。
  • UX_MAX_HCD:此值表示系统中可用的不同主机控制器的数量。 对于 USB 1.1 支持,此值主要设置为 1。 对于 USB 2.0 支持,此值可以大于 1。 此值表示同时运行的并发主机控制器的数量。 例如,如果有两个 OHCI 实例正在运行,或者一个 EHCI 控制器和一个 OHCI 控制器正在运行,则 UX_MAX_HCD 应设置为 2。
  • UX_MAX_DEVICES:此值表示可连接到 USB 接口的最大设备数。 通常,理论上单个 USB 接口可连接的最大设备数为 127。 可以缩减此值来节省内存。 应注意的是,无论系统中的 USB 总线数量为多少,此值都表示设备总数。
  • UX_MAX_ED:此值表示控制器池中的最大 ED 数目。 此数目仅分配给一个控制器。 如果存在多个控制器实例,则每个控制器都将使用此值。
  • UX_MAX_TD and UX_MAX_ISO_TD:此值表示控制器池中常规 TD 和常时等量 TD 的最大数目。 此数目仅分配给一个控制器。 如果存在多个控制器实例,则每个控制器都将使用此值。
  • UX_THREAD_STACK_SIZE:此值为 USBX 线程的堆栈大小(以字节为单位)。 它通常可以是 1024 字节或 2048 字节,具体取决于所用的处理器和主机控制器。
  • UX_HOST_ENUM_THREAD_STACK_SIZE:此值为 USB 主机枚举线程的堆栈大小。 如果未设置此符号,则 USBX 主机枚举线程堆栈大小设置为 UX_THREAD_STACK_SIZE。
  • UX_HOST_HCD_THREAD_STACK_SIZE:此值为 USB 主机 HCD 线程的堆栈大小。 如果未设置此符号,则 USBX 主机 HCD 线程堆栈大小设置为 UX_THREAD_STACK_SIZE。
  • UX_THREAD_PRIORITY_ENUM:这是用于监视总线拓扑的 USBX 枚举线程的 ThreadX 优先级值。
  • UX_THREAD_PRIORITY_CLASS:这是标准 USBX 线程的 ThreadX 优先级值。
  • UX_THREAD_PRIORITY_KEYBOARD:这是 USBX HID 键盘类的 ThreadX 优先级值。
  • UX_THREAD_PRIORITY_HCD:这是主机控制器线程的 ThreadX 优先级值。
  • UX_NO_TIME_SLICE:此值实际上定义将用于线程的时间片。 例如,如果定义为 0,则 ThreadX 目标端口不使用时间片。
  • UX_MAX_HOST_LUN:此值表示主机存储类驱动程序中表示的最大 SCSI 逻辑单元数。
  • UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT:如果定义此选项,则此值包含用于处理使用 CB 或 CBI 协议的存储设备(例如软盘)的代码。 默认关闭此支持,因为这些协议已过时并由仅批量传输 (Bulk Only Transport, BOT) 协议取代,几乎所有现代存储设备都使用该协议。
  • UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE:如果定义此选项,则此值会导致 ux_host_class_hid_keyboard_key_get 仅报告按键更改,即按下按键和释放按键。 默认情况下,它仅在某个按键已按住时进行报告。
  • UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_KEY_DOWN_ONLY:仅在定义了 UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE 时使用。 如果定义此选项,将导致 ux_host_class_hid_keyboard_key_get 仅报告与按下/按住按键相关的更改,而不报告与释放按键相关的更改。
  • UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_LOCK_KEYS:仅在定义了 UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE 时使用。 如果定义此选项,将导致 ux_host_class_hid_keyboard_key_get 报告与锁定键 (CapsLock/NumLock/ScrollLock) 相关的更改。
  • UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_MODIFIER_KEYS:仅在定义了 UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE 时使用。 如果定义此选项,将导致 ux_host_class_hid_keyboard_key_get 报告与修改键 (Ctrl/Alt/Shift/GUI) 相关的更改。
  • UX_HOST_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES:如果定义此选项,则此值表示 CDC-ECM 主机类中的数据包数。 默认值为 16。

源代码树

USBX 文件在多个目录中提供。

Source Code Tree

为了使文件可通过其名称识别,已采用以下约定:

文件后缀名 文件说明
ux_host_stack usbx 主机堆栈核心文件
ux_host_class usbx 主机堆栈类文件
ux_hcd usbx 主机堆栈控制器驱动程序文件
ux_device_stack usbx 设备堆栈核心文件
ux_device_class usbx 设备堆栈类文件
ux_dcd usbx 设备堆栈控制器驱动程序文件
ux_otg usbx otg 控制器驱动程序相关文件
ux_pictbridge usbx pictbridge 文件
ux_utility usbx 实用工具函数
demo_usbx USBX 的演示文件

USBX 资源的初始化

USBX 有自己的内存管理器。 在初始化 USBX 的主机或设备端之前,需要将内存分配给 USBX。 USBX 内存管理器可以容纳可缓存内存的系统。

以下函数可将 USBX 内存资源初始化为具有 128K 常规内存且没有用于缓存安全内存的单独池:

/* Initialize USBX Memory */

ux_system_initialize(memory_pointer,(128*1024),UX_NULL,0);

ux_system_initialize 的原型如下所示。

UINT ux_system_initialize( 
    VOID *regular_memory_pool_start,
    ULONG regular_memory_size,
    VOID *cache_safe_memory_pool_start,
    ULONG cache_safe_memory_size);

输入参数:

  • regular_memory_pool_start:常规内存池的开头。
  • regular_memory_size:常规内存池的大小。
  • cache_safe_memory_pool_start:缓存安全内存池的开头。
  • cache_safe_memory_size:缓存安全内存池的大小。 |

并非所有系统都需要定义缓存安全内存。 在此类系统中,在初始化过程中为内存指针传递的值将设置为 UX_NULL,并且池的大小会设置为 0。 然后,USBX 将使用常规内存池来代替缓存安全池。

在常规内存不是缓存安全内存的系统中,如果控制器(例如,OHCI 控制器、EHCI 控制器及其他控制器)需要执行 DMA 内存,则需要在缓存安全区域中定义一个内存池。

USBX 资源的取消初始化

可以通过释放 USBX 的资源来将其终止。 在终止 USBX 之前,需要正确终止所有类和控制器资源。 以下函数将取消初始化 USBX 内存资源:

/* Unitialize USBX Resources */

ux_system_uninitialize();

ux_system_initialize 的原型如下所示。

UINT ux_system_uninitialize(VOID);

USB 主机控制器的定义

需要定义至少一个 USB 主机控制器,USBX 才能在主机模式下运行。 应用程序初始化文件应包含此定义。 以下行将定义通用主机控制器。

ux_host_stack_hcd_register("ux_hcd_controller",
        ux_hcd_controller_initialize, 0xd0000, 0x0a);

ux_host_stack_hcd_register 具有以下原型。

UINT ux_host_stack_hcd_register(
    UCHAR *hcd_name,
    UINT (*hcd_initialize_function)(struct UX_HCD_STRUCT *),
    ULONG hcd_param1, ULONG hcd_param2);

ux_host_stack_hcd_register 函数具有以下参数。

  • hcd_name:控制器名称的字符串
  • hcd_initialize_function:控制器的初始化函数
  • hcd_param1:通常是控制器使用的 IO 值或内存
  • hcd_param2:通常是控制器使用的 IRQ

在上述示例中:

  • “ux_hcd_controller”是控制器的名称
  • “ux_hcd_controller_initialize”是主机控制器的初始化例程
  • 0xd0000 是主机控制器寄存器在内存中多位于的地址
  • 0x0a 是主机控制器使用的 IRQ。

下面是将处于主机模式的 USBX 初始化为具有一个主机控制器和多个类的示例。

UINT status;

/* Initialize USBX. */
ux_system_initialize(memory_ptr, (128*1024),0,0);

/* The code below is required for installing the USBX host stack. */
status = ux_host_stack_initialize(UX_NULL);

/* If status equals UX_SUCCESS, host stack has been initialized. */

/* Register all the host classes for this USBX implementation. */
status = ux_host_class_register("ux_host_class_hub", ux_host_class_hub_entry);

/* If status equals UX_SUCCESS, host class has been registered. */
status = ux_host_class_register("ux_host_class_storage", ux_host_class_storage_entry);

/* If status equals UX_SUCCESS, host class has been registered. */
status = ux_host_class_register("ux_host_class_printer", ux_host_class_printer_entry);

/* If status equals UX_SUCCESS, host class has been registered. */
status = ux_host_class_register("ux_host_class_audio", ux_host_class_audio_entry);

/* If status equals UX_SUCCESS, host class has been registered. */

/* Register all the USB host controllers available in this system. */ 
status = ux_host_stack_hcd_register("ux_hcd_controller", ux_hcd_controller_initialize, 0x300000, 0x0a);

/* If status equals UX_SUCCESS, USB host controllers have been registered. */

主机类的定义

需要随 USBX 一起定义一个或多个主机类。 USB 堆栈配置了 USB 设备后,需要定义 USB 类来驱动 USB 设备。 USB 类特定于设备。 可能需要定义一个或多个类来驱动 USB 设备,具体取决于 USB 设备描述符中包含的接口数量。

下面是注册集线器类的示例。

status = ux_host_stack_class_register("ux_host_class_hub", ux_host_class_hub_entry);

函数 ux_host_class_register 具有以下原型。

UINT ux_host_stack_class_register(
    UCHAR *class_name, 
    UINT (*class_entry_address) (struct UX_HOST_CLASS_COMMAND_STRUCT *));
  • class_name 是类的名称
  • class_entry_address 是类的入口点

在集线器类初始化的示例中:

  • “ux_host_class_hub”是集线器类的名称
  • ux_host_class_hub_entry 是集线器类的入口点。

访问主机类函数实例

类注册和主机控制器驱动程序 (HCD) 注册后,可以检测并枚举连接到 USB 端口的 USB 设备。 在枚举过程中,已注册的类会检查连接的设备信息,如果找到正确的信息,则为类函数实例分配内存,然后激活该实例。 应用程序使用该实例来执行特定于类函数的操作。

从 USB 端口中删除 USB 设备时,将停用激活的类函数实例。

可通过两种方式访问主机实例:从已注册的类和/或通过主机更改回调通知。

通过已注册的类获取实例

激活类函数实例时,它将链接到拥有该实例的类。 因此,可以通过类访问实例。 例如,检查 storage 实例是否可用:

    /* Find the main storage container (class)  */
    status =  ux_host_stack_class_get(_ux_system_host_class_storage_name, &ux_class);
    if (status != UX_SUCCESS)
        return(status);

    /* Find the storage instance under container (class)  */
    status =  ux_host_stack_class_instance_get(ux_class, 0, (void **) &storage);
    if (status == UX_SUCCESS)
    {
        /* Check if storage state, storage specific resources are ready.  */
        if (storage -> ux_host_class_storage_state == UX_HOST_CLASS_INSTANCE_LIVE &&
            ux_class -> ux_host_class_ext != UX_NULL &&
            ux_class -> ux_host_class_media != UX_NULL)
            return(UX_SUCCESS);
    }

ux_host_stack_class_get 用于获取已注册的类,给定用于注册的类名。

ux_host_stack_class_instance_get 用于从特定注册类获取函数实例。

例如,检查实例的状态和其他一些与函数相关的资源,以确认实例已准备好使用。

通过主机更改回调通知获取实例

有一个可选的主机更改回调函数,该函数在主机堆栈初始化上分配。 如果函数可用,则当函数实例被激活且状态良好且具有所有可用资源时,将调用该函数。 在回调中传递实例,在应用程序回调中用户可以将其保存以供进一步操作。

有关详细信息,请参阅 ux_host_stack_initialize

故障排除

USBX 附带演示文件和模拟环境。 最好先让演示平台在目标硬件或特定演示平台上运行。

如果演示系统无法工作,请尝试通过以下方法缩小问题的范围。

USBX 版本 ID

当前版本的 USBX 在运行时可供用户和应用程序软件使用。 程序员可以通过检查 ux_port.h 文件来获取 USBX 版本。 此外,该文件还包含相应端口的版本历史记录。 应用程序软件可以通过检查全局字符串 ux_version_id(在 ux_port.h 中定义)来获取 USBX 版本。