mchp usb device stack -...

38
2009 Microchip Technology Inc. DS01176A_CN 1 AN1176 引言 通用串行总线 (Universal Serial BusUSB)已经彻 底改变了外部设备与个人计算机 (PC)的连接方式。 USB 提供了一种简单的公共接口,可用于所能想象得到 的几乎任何类型的外部设备。用户简单地把外部设备插 入计算机的某个 USB 端口,或者插入与计算机相连的 采用公共插头的集线器,安装驱动程序(如果操作系统 还没有支持这个外部设备),然后设备就可以使用了。 USB 的灵活性和强大功能要求对用于设备标识、配置、 控制和数据传输的协议进行管理。 Microchip PIC32 USB 设备固件栈提供了一种易于使用的框架,可简化使 用受支持的 Microchip 单片机系列开发与 USB 2.0 兼容 的外部设备。 本应用笔记描述了 Microchip PIC32 USB 设备固件栈。 对于因没有现成的 Microchip 样本实现可用的任何 USB 外部设备类型而需设计固件的开发人员而言,本文档可 用作编程参考手册。文中描述了如何实现功能特定的驱 动程序,以便与 Microchip USB 设备固件栈进行连接; 并且展示了这将如何简化整个应用的开发。 假设 1. 具备 C 程序设计语言的实用知识 2. 熟悉 USB 2.0 协议 3. 熟悉 Microchip MPLAB ® IDE 功能与特性 支持 USB 外部设备应用 处理标准的 USB 设备请求,如 Universal Serial Bus Specification, Revision 2.09 章所述 http://www.usb.org/developers/docs/支持多个配置和接口 简化 USB 描述符定义和配置信息 可选择支持备用接口设置 支持多功能设备 事件驱动系统 (中断或查询) 提供简单的应用程序编程接口 (Application Pro- gram InterfaceAPI提供简单的功能驱动程序接口 (Function Driver InterfaceFDI限制 最多允许支持 32 USB 端点 (16 IN 16 OUT最多支持 32 个设备功能 可能的配置数量仅受限于可用的存储容量 系统硬件 USB 固件栈的开发,针对的是下列硬件: 支持 USB PIC32 单片机 作者: Bud Caldwell Microchip Technology Inc. PIC32 USB 设备栈编程指南

Upload: others

Post on 19-Oct-2020

6 views

Category:

Documents


0 download

TRANSCRIPT

  • AN1176PIC32 USB 设备栈编程指南

    引言

    通用串行总线 (Universal Serial Bus, USB)已经彻底改变了外部设备与个人计算机 (PC)的连接方式。USB 提供了一种简单的公共接口,可用于所能想象得到的几乎任何类型的外部设备。用户简单地把外部设备插入计算机的某个 USB 端口,或者插入与计算机相连的采用公共插头的集线器,安装驱动程序(如果操作系统还没有支持这个外部设备),然后设备就可以使用了。

    USB 的灵活性和强大功能要求对用于设备标识、配置、控制和数据传输的协议进行管理。 Microchip PIC32USB 设备固件栈提供了一种易于使用的框架,可简化使用受支持的 Microchip 单片机系列开发与 USB 2.0 兼容的外部设备。

    本应用笔记描述了 Microchip PIC32 USB 设备固件栈。对于因没有现成的 Microchip 样本实现可用的任何 USB外部设备类型而需设计固件的开发人员而言,本文档可用作编程参考手册。文中描述了如何实现功能特定的驱动程序,以便与 Microchip USB 设备固件栈进行连接;并且展示了这将如何简化整个应用的开发。

    假设

    1. 具备 C 程序设计语言的实用知识 2. 熟悉 USB 2.0 协议 3. 熟悉 Microchip MPLAB® IDE

    功能与特性

    • 支持 USB 外部设备应用• 处理标准的 USB 设备请求,如“Universal Serial

    Bus Specification, Revision 2.0”第 9 章所述(http://www.usb.org/developers/docs/)

    • 支持多个配置和接口• 简化 USB 描述符定义和配置信息• 可选择支持备用接口设置• 支持多功能设备• 事件驱动系统 (中断或查询)• 提供简单的应用程序编程接口 (Application Pro-

    gram Interface, API)• 提供简单的功能驱动程序接口 (Function Driver

    Interface, FDI)

    限制

    • 最多允许支持 32 个 USB 端点(16 个 IN和 16 个OUT)

    • 最多支持 32 个设备功能• 可能的配置数量仅受限于可用的存储容量

    系统硬件

    USB 固件栈的开发,针对的是下列硬件:

    • 支持 USB 的 PIC32 单片机

    作者: Bud CaldwellMicrochip Technology Inc.

    2009 Microchip Technology Inc. DS01176A_CN 第 1 页

    http://www.usb.org/developers/docs/

  • AN1176

    PIC® MCU 存储器资源需求关于整个程序和数据存储器需求,请参阅安装目录下的版本说明。

    PIC® MCU 硬件资源需求Microchip USB 设备栈固件使用下列 I/O 引脚:

    安装源文件

    完整的设备栈源文件可以从 Microchip 网站下载(见附录 D:USB 设备栈编程指南的源代码)。源代码以Microsoft Windows® 安装文件的形式提供。

    执行下列步骤,完成安装:

    1. 执行安装文件。Windows 安装向导将指导您完成安装过程。

    2. 在继续安装之前,必须点击 I Accept(我接受),接受软件许可协议。

    3. 安装过程完成之后,在“PIC32 Solutions”程序组下,应看到一个新条目 PIC32 USB DeviceStack。

    4. 阅读版本说明,了解最新版本提供的功能特性与限制。

    表 1: PIC® MCU I/O 引脚用途 I/O 引脚 用途

    D+ (IO) USB D+ 差动数据信号D- (IO) USB D- 差动数据信号

    VBUS( 输入) 检测 USB 电源 (不工作在总线供电方式下)

    VUSB( 输入) USB D+/D- 收发器的电源输入

    DS01176A_CN 第 2 页 2009 Microchip Technology Inc.

  • AN1176

    源文件构成

    Microchip USB 设备栈包含下列源文件和头文件:

    * 除非选择了其他位置,否则默认的安装根目录是“C:\PIC32 Solutions”。

    表 2: 源文件文件 目录 * 说明

    usb_device.c Microchip\USB USB 设备层 (设备抽象和第 9 章的协议处理)

    usb_hal.c Microchip\USB USB 硬件抽象层(Hardware Abstraction Layer,HAL)接口支持

    usb_hal_core.c Microchip\USB HAL 接口支持使用的 USB 控制器函数

    usb_device_local.h Microchip\USB USB 设备层的私有定义

    usb_hal_core.h Microchip\USB HAL 控制器内核的私有定义

    usb_hal_local.h Microchip\USB HAL 的私有定义

    usb.h Microchip\Include\USB 总的 USB 头文件 (包括所有其他的 USB 头文件)

    usb_ch9.h Microchip\Include\USB USB 设备框架(“Universal Serial Bus Specification, Revision 2.0” 第 9 章 )定义

    usb_common.h Microchip\Include\USB 公共的 USB 协议栈定义

    usb_device.h Microchip\Include\USB USB 设备层接口定义

    usb_hal.h Microchip\Include\USB USB HAL 接口定义

    usb_config.h 应用特定的配置选项 (见附录 A:USB 固件栈配置)

    2009 Microchip Technology Inc. DS01176A_CN 第 3 页

  • AN1176

    演示应用

    本文档并非是具体的演示应用。文档旨在说明:在没有适合期望用途的样本应用可利用时,如何使用 USB 设备栈。 Microchip 已提供的样本应用,请参见 “ 参考文献 ”。

    协议栈架构

    可以把 USB 设备固件栈看成由 4 层组成,如图 1 所示。

    图 1: 概念化的协议栈

    应用程序

    应用程序由实现设备期望行为所必需的固件组成。它是

    由用户设计和实现的代码,尽管它可能是基于Microchip提供的样本代码。根据需要,代码可以和USB固件栈或系统中任何其他软件进行通信。

    USB 功能驱动程序每个 USB 外部设备实现一个具体的功能(打印机、鼠标和海量存储,等等)。有些设备可能有多重功能。功能驱动程序实现期望的功能行为,并且为应用提供功能特定的控制接口。要访问 USB 及传送数据和控制信息,功能驱动程序应与 USB 设备层进行交互。

    Microchip 针对若干最常用的 USB 设备功能提供了功能驱动程序。然而,可能也需要定制开发特定的 USB 功能驱动程序 (这正是本文档的主题)。

    USB 设备设备层对 USB 设备进行抽象。关于设备可能实现了什么功能,设备层并没有任何假设。它的主要工作是处理USB 协议元(element), USB 协议元在 “UniversalSerial Bus Specification, Revision 2.0”的第 9 章有定义。设备层还提供了任何类型的功能可能需要的对 USB的所有访问途径。访问途径的实现方法如下:通过实现一个定义明确的接口 (将在本文档中描述),所有的功能驱动程序都使用此接口来访问 USB。然后,设备层与HAL 进行通信,以支持此接口。

    HALHAL(硬件抽象层)对 USB 控制器硬件进行抽象。对于控制器实现的支持USB外部设备的全部功能特性,HAL提供了对其的访问途径。

    注: USB 描述符和其他配置选项是应用特定的。因此,它们需要通过应用进行定义。应用必须提供一个函数,USB 固件栈调用该函数来检索这些定义。该函数必须定义在usb_config.h 头文件中(参见USB_DEV_GET_DESCRIPTOR_FUNC)。

    应用程序

    功能驱动程序

    功能驱动程序

    功能驱动程序

    USB 设备固件栈

    设备层

    HAL

    ••••

    DS01176A_CN 第 4 页 2009 Microchip Technology Inc.

  • AN1176

    创建 USB 应用本节讲述:设计和实现 USB 外部设备应用所需的步骤,如何实现功能驱动程序以及如何把功能程序与Microchip PIC32 USB 设备固件栈进行整合。

    概要:

    1. 实现主应用程序。2. 实现 USB 功能驱动程序。3. 实现应用特定的 USB 支持。4. 配置 USB 协议栈选项。

    实现主应用程序

    使用 MPLAB IDE,为受支持的单片机创建一个新的应用程序。(关于如何创建项目的说明,请参考 MPLABIDE 联机帮助。)实现并测试任何期望的应用特定的非USB 支持。

    为支持 USB 固件栈,在任何其他 USB 活动发生之前,应用的主函数必须调用 USBInitialize一次。在调用USBInitialize之后,应用在“查询”循环中必须调用 USBTasks(如例 1 所示) ,或者应用必须把USBTasks 直接链接到处理器的 USB 中断服务例程(Interrupt Service Routine, ISR)。

    例 1: 主应用程序逻辑

    应用程序和功能驱动程序之间的接口完全取决于功能驱动程序的设计人员,所以接口不在本文档的讨论范围内。然而,建议应用程序实现一个事件例程,该例程类似于功能驱动程序接口(FDI)定义的 Func-EventHandling Routine(功能事件处理例程),以便接收来自 USB 协议栈的事件。如果事件例程已经实现,可以设计其功能驱动程序,以调用应用程序并向应用程序传递事件,方法类似于驱动程序从 USB 协议栈接收事件。(参见 “ 实现 USB 功能驱动程序 ”。)

    实现 USB 功能驱动程序USB 功能驱动程序是为了实现类或供应商特定的 USB功能。功能驱动程序必须有接口与 FDI 例程相连,以便在 USB 上传送数据和接收发生在总线上的事件通知。

    初始化

    在主机选择好设备配置之后, USB 协议栈将对功能驱动程序进行初始化。为此,驱动程序必须实现一个初始化例程,它具有确定的 C 语言函数签名(signature)。初始化例程的调用通过 “功能驱动程序表 ”中的指针来进行。

    初始化例程(见例 2)是为了复位并初始化功能驱动程序的状态,为总线上任何功能特定的行为做好准备。

    例 2: 初始化例程

    注意,示例例程所做的一切是初始化数据结构,这是维护函数状态(gGenFunc)的数据结构。根据应用特定的配置表,通过 USB 固件栈处理端点硬件的实际初始化。

    // 初始化 USB协议栈 USBInitialize(0);

    // 主处理循环 while(1) {

    // 检查 USB事件并适当地处理它们

    USBHandleEvents();

    // 进行所需的任何// 附加的 IO处理

    }

    注: 在查询循环中的代码执行,必须是无阻塞

    式的,或者在任何情况下等待时间不得超过几微秒。如果需要阻塞行为,则 USB 固件栈必须用在基于中断的环境中。

    如果在基于中断的环境中执行,功能驱动程序的事件处理例程 (以及应用程序的事件处理例程,如果实现了的话)将在中断情境中进行调用。

    BOOL USBGenInitialize ( unsigned long flags { // 初始化驱动程序状态 memset(&gGenFunc, sizeof(gGenFunc), 0);

    // 设置初始化标志! gGenFunc.flags =

    GEN_FUNC_FLAG_INITIALIZED;

    return TRUE;}

    2009 Microchip Technology Inc. DS01176A_CN 第 5 页

  • AN1176

    事件处理

    功能驱动程序必须做的另一件事,是处理发生在USB上的设备或功能特定的事件。为此,驱动程序必须实现一个事件处理例程,它具有确定的 C 语言函数签名。较低级别的 USB 协议栈使用功能驱动程序表中的指针来调用该例程。

    事件处理例程是为了响应相应的事件,并提供所需要的行为。事件通过 USB_EVENT 枚举数据类型进行定义,枚举数据类型在 usb_common.h 头文件中。功能驱动程序的事件处理例程必须执行正确动作以支持期望的功能行为。至于对每个事件必须采取什么样的动作是功能特定的,超出本文档的范围。对于期望的 USB 外部设备功能,为了实现其驱动程序,功能驱动程序设计人员必须透彻理解所需行为的知识。

    例 3 说明功能驱动程序何时将调用 USBDEVGetLas-tError FDI 例程。当协议栈把一个EVENT_BUS_ERROR事件“扔”给功能驱动程序时,必须调用这个例程,以便清除错误指示位。协议栈自己将直接处理大部分这些事件。然而,如果事件不是由协议栈来处理的,那么功能驱动程序可能需要采取错误特定的及功能特定的动作。

    大部分功能驱动程序需要做的另一件事是在 USB 上传输数据。数据的传输是通过调用 USBDEVTransfer-Data FDI例程来完成的。调用此例程的常见原因是何时接收到功能特定的请求 ——EVENT_TRANSFER 事件指出已经接收到请求,并且请求已经被解码成功能特定的请求以便传输数据。然后,驱动程序的事件处理逻辑可能需要调用 USBDEVTransferData例程来满足请求。关于如何使用这些 FDI 函数示例,请参阅它们在附录C:“USB 功能驱动程序接口 ” 中的描述。

    例 3: 事件处理例程

    BOOL USBGenEventHandler ( USB_EVENT event, void *data, unsigned int size ){ unsigned long error;

    // 如果没有初始化,则异常中止 if ( !(gGenFunc.flags & GEN_FUNC_FLAG_INITIALIZED) ) { return FALSE; }

    // 处理具体事件 switch (event) { case EVENT_TRANSFER: // USB传输已完成 return HandleTransferDone((USB_XFER_EVT_DATA *)data);

    case EVENT_DETACH: // USB电缆已断开 // 取消通用功能驱动程序的初始化 gGenFunc.flags = 0; gGenFunc.rx_size = 0; return TRUE;

    case EVENT_BUS_ERROR: //总线上错误 error = USBDEVGetLastError(); //将捕获错误并且进行相应处理 return TRUE;

    // 处理应用程序或功能所需的任何其他事件

    default: // 未知事件 return FALSE; }}

    注: 在事件处理例程环境中执行的代码,必须是非阻塞式的。

    DS01176A_CN 第 6 页 2009 Microchip Technology Inc.

  • AN1176

    实现应用特定的 USB 支持为了把一个或多个功能驱动程序集成到USB固件栈,为了给应用配置协议栈,用户必须定义三张表并实现函数(或宏),以便为 USB 设备栈提供访问它们的途径。

    应用特定的 USB 表:

    1. USB 描述符表 2. 端点配置表3. 支持的功能驱动程序表

    所有三张表是相互关联的。合在一起,它们标识 USB 协议栈和设备本身支持的特性、端点配置和功能。

    USB 描述符的要求在 “Universal Serial Bus Specifica-tion 2.0”中有定义。对于用户可能打算设计的几乎任何类型的设备,USB 描述符的要求还定义在类特定的补充说明中。 “ 实现 USB 描述符表 ” 所述的方法可实现这些描述符,并为 USB 固件栈提供访问它们的途径。

    有了端点配置表和功能驱动程序表,应用能够支持的设备的端点配置数可以是任意的,其 USB 外部设备功能的数量几乎也是任意的。端点配置表表明:对于每个端点 (对于每个配置、接口和备用接口设置),哪一个功能驱动程序将接收其事件。功能驱动程序表的图示说明如图 2 所示。箭头表示端点配置表表项和功能驱动程序表表项之间的关系。

    后续各节将对这些表进行详细说明,还将说明它们的实现方法以及访问例程。

    图 2: 端点配置表和功能驱动程序表

    配置 # 接口 # 备用接口 # EP # EP 配置数据 功能 #

    1 0 0 1 Tx, HndShk 0

    1 0 0 2 Rx, HndShk 0

    1 0 0 3 Tx, HndShk 0

    1 1 0 5 Tx, HndShk 1

    1 1 0 7 Rx, HndShk 1

    1 1 0 9 Rx, HndShk 1

    1 1 1 5 Tx 2

    1 1 1 7 Rx 2

    1 1 1 9 Rx 2

    2 0 0 1 Tx, HndShk 0

    2 0 0 2 Rx, HndShk 0

    2 0 0 3 Tx, HndShk 0

    HID

    音频

    批量传输

    功能驱动

    0

    1

    2

    程序表

    2009 Microchip Technology Inc. DS01176A_CN 第 7 页

  • AN1176

    实现 USB 描述符表

    每个 USB 设备都必须提供一组描述符(数据结构), 对设备进行说明,并向主机提供如何使用设备的细节。至于这些描述符是如何正确提供的以及其中必须包含什么信息,在“Universal Serial Bus Specification, Revision2.0” 的第9章及其类特定的补充说明中有定义。请参阅这些文档,了解完整的细节。在 Microchip USB 协议栈中, 使用 usb_ch9.h头文件中定义的数据类型来产生这些描述符。

    可以认为 USB 描述符属于以下三个不同描述符组中的一个:它们是描述总体设备的组,描述可能的设备配置的组以及提供用户可读取信息的组。每个 USB 设备有且仅有一个设备描述符。它用来唯一地标识设备,给出可能配置的数量。每个配置都有其自身的描述符集,对配置细节进行说明。可能还存在数量任意的用户可读取的“ 字符串” 描述符。

    例 4 说明:如何使用为 USB 描述符提供的类型定义来何定义 USB 描述符表。

    例 4: 描述符表定义

    #define NUM_LANGS 1#define LANG_1_ID 0x0409 // 英语#define STR_1_LEN 25#define STR_2_LEN 27#define STR_3_LEN 10

    typedef struct _config1_descriptors{ USB_CONFIGURATION_DESCRIPTOR cfg_desc; // 配置 1 USB_INTERFACE_DESCRIPTOR intf0_desc; // 配置 1, 接口 0 USB_ENDPOINT_DESCRIPTOR intf0_ep1_in_desc; // 端点 0 输入 (Tx) USB_ENDPOINT_DESCRIPTOR intf0_ep1_out_desc; // 端点 0 输出 (Rx)} CONFIG1_DESC, *PCONFIG1_DESC;

    typedef struct _string0_descriptor{ USB_STRING_DESCRIPTOR string; // String0 描述符 WORD langid[NUM_LANGS];} STR0_DESC, *PSTR_DESC;

    typedef struct _string1_descriptor{ USB_STRING_DESCRIPTOR string; // String1 描述符 WORD string_data[STR_1_LEN];} STR1_DESC, *PSTR1_DESC;

    typedef struct _string2_descriptor{ USB_STRING_DESCRIPTOR string; // String2 描述符 WORD string_data[STR_2_LEN];} STR2_DESC, *PSTR2_DESC;

    typedef struct _string3_descriptor{ USB_STRING_DESCRIPTOR string; // String3 描述符 WORD string_data[STR_3_LEN];

    } STR3_DESC, *PSTR3_DESC;

    注: 没有针对端点 0 的端点描述符。 “Universal Serial Bus Specification, Revision 2.0” 明确地定义了端点 0 的行为。仅有包的大小是可配置的。

    DS01176A_CN 第 8 页 2009 Microchip Technology Inc.

  • AN1176

    可以单独给出 “ 设备 ” 描述符(它使用 usb_ch9.h中给出的标准定义)。必须把配置描述符(cfg_desc、intf0_desc、 intf0_ep1_in_desc 和intf0_ep1_out_desc)作为一组连续的描述符一起给出。主机将请求其希望接收的数据的具体数量,以及希望接受哪一组配置描述符的索引 (从零开始)。还将根据索引(string 0 和 string 1,等等)以及语言 ID,请求字符串描述符。 使用例 4 所示的描述符表定义,例 5 展示了如何初始化

    描述符表。

    例 5: 描述符表初始化USB_DEVICE_DESCRIPTOR dev_desc = { sizeof(USB_DEVICE_DESCRIPTOR), // 描述符的大小,以字节为单位 USB_DESCRIPTOR_DEVICE, // 设备描述符类型 0x0200, // USB规范版本编号, BCD格式 0x00, // 类代码 0x00, // 子类代码 0x00, // 协议代码 USB_DEV_EP0_MAX_PACKET_SIZE, // EP0的最大包大小,见 usbcfg.h 0x04D8, // 供应商 ID 0x000C, // 产品 ID: PICDEM FS USB(演示模式) 0x0000, // 设备版本编号, BCD格式 0x01, // 制造商字符串索引 0x02, // 产品字符串索引 0x00, // 设备序列号字符串索引 0x01 // 可能的配置数量};

    CONFIG1_DESC config1 ={ { /* 配置描述符 */ sizeof(USB_CONFIGURATION_DESCRIPTOR),// 描述符的大小,以字节为单位 USB_DESCRIPTOR_CONFIGURATION, // 配置描述符类型 sizeof(CONFIG1_DESC), // 配置数据的总长度 1, // 配置中的接口数量 USBGEN_CONFIG_NUM, // 配置的索引值 0, // 配置字符串索引 0x01

  • AN1176

    { /* EP 1 - 输入 */ sizeof(USB_ENDPOINT_DESCRIPTOR), USB_DESCRIPTOR_ENDPOINT, {EP_DIR_IN|USBGEN_EP_NUM}, {EP_ATTR_INTR}, EP_MAX_PKT_INTR_FS, 32 }};

    STR0_DESC string0 ={ { // 语言 ID:英语 sizeof(STR0_DESC), USB_DESCRIPTOR_STRING }, {LANG_1_ID}};

    STR1_DESC string1 ={ { // 供应商描述 sizeof(STR1_DESC), USB_DESCRIPTOR_STRING }, {'M','i','c','r','o','c','h','i','p',' ', 'T','e','c','h','n','o','l','o','g','y',' ', 'I','n','c','.'}};

    STR2_DESC string2 ={ { // 设备描述 sizeof(STR2_DESC), USB_DESCRIPTOR_STRING }, {'P','I','C','3','2',' ','P','I','C','D','E','M',' ', 'D','e','m','o',' ','E','m','u','l','a','t','i','o','n'}};

    STR3_DESC string3 ={ { // 序列号 sizeof(STR3_DESC), USB_DESCRIPTOR_STRING }, {'0','0','0','0','0','0','0','0','0','0'}};

    除了必需的描述符组之外,应用还必须提供访问它们的例程。例 6 展示了访问例程的一种实现。访问例程的 C 语言函数签名,必须与 “ 应用程序编程接口 ” 中 USB API——USB_DEV_GET_DESCRIPTOR_FUNC定义所描述的完全一致。

    DS01176A_CN 第 10 页 2009 Microchip Technology Inc.

  • AN1176

    例 6: 获取描述符例程及支持代码

    static inline const void *GetConfigurationDescriptor( BYTE config, unsigned int *length ){ switch (config) { case 0: // 配置 1(默认) *length = sizeof(config1); return &config1; default: return NULL; }

    } // GetConfigurationDescriptor

    static inline const void *GetStringDescriptor( PDESC_ID desc, unsigned int *length ){ // 检查语言 ID if (desc->lang_id != LANG_1_ID) { return NULL; }

    // 获取请求的字符串 switch(desc->index) { case 0: // 字符串 0 *length = sizeof(string0); return &string0;

    case 1: // 字符串 1 *length = sizeof(string1); return &string1;

    case 2: // 字符串 2 *length = sizeof(string2); return &string2;

    case 3: // 字符串 3 *length = sizeof(string3); return &string3;

    default: return NULL; }

    } // GetStringDescriptor

    const void *USBDEVGetDescriptor ( PDESC_ID desc, unsigned int *length ){ switch (desc->type) { case USB_DESCRIPTOR_DEVICE: // 设备描述符 *length = sizeof(dev_desc); return &dev_desc;

    case USB_DESCRIPTOR_CONFIGURATION:// 配置描述符 return GetConfigurationDescriptor(desc->index, length);

    case USB_DESCRIPTOR_STRING: // 字符串描述符 return GetStringDescriptor(desc, length);

    2009 Microchip Technology Inc. DS01176A_CN 第 11 页

  • AN1176

    // 对于所有未支持的描述符请求,失败

    default: return NULL; }

    } // USBDEVGetDescriptor

    注: 在例 6 中,使用内联辅助函数 GetStringDescriptor 和 GetConfigurationDescriptor 来实现USBDEVGetDescriptor例程,这样代码可读性更高,且不会产生函数调用开销。

    DS01176A_CN 第 12 页 2009 Microchip Technology Inc.

  • AN1176

    实现端点配置表

    对于每个受支持的 USB 设备配置,使用端点配置表,按照每个接口或备用设置,指明所需的每个端点的方向和协议特性。端点配置表还表明:对于每个端点上发生的事件,应使用哪个功能驱动程序提供服务。唯一的例外是端点 0,协议栈自动配置端点 0,它不在端点配置表中。

    EP_CONFIG结构和标志定义在 usb_device.h 头文件中。

    配置表中的每一项都由下面的数据结构组成:

    图 3: 端点配置表结构

    max_pkt_size 字段定义在单个包中端点能够传输多少字节。 ep_num 字段标识结构描述的是哪个端点。config、intf和alt_intf字段标识结构描述的是哪个设备配置、接口和备用接口设置。function字段标识哪个功能驱动程序使用 ep_num 所标识的端点。为此,要提供指向 “ 支持的功能驱动程序表 ” 的索引(如图 2 中用箭头表示)。 flags字段提供配置端点行为的信息。标志的说明见表 3。

    术语

    在讨论总线上数据流的方向时,术语可能有些混乱。

    USB 规范使用术语 OUT,表示数据流的方向是从主机(PC)到设备(外部设备),使用术语 IN 来表示数据流的方向是从设备到主机。

    由于单片机上的 USB 接口也可以支持 USB 主机功能,Microchip PIC32 USB 协议栈使用术语 TRANSMIT(发送),表示数据流出单片机 (到总线上),使用术语RECEIVE(接收)表示数据从 USB 流入单片机。为了帮助理解,下表总结了这些术语之间的关系。

    typedef struct { UINT16 max_pkt_size; UINT16 flags; BYTE config; BYTE ep_num; BYTE intf; BYTE alt_intf; BYTE function;

    } EP_CONFIG, *PEP_CONFIG;

    表 3: 端点配置标志标志 说明

    USB_EP_TRANSMIT 使能端点,从而能够发送数据

    USB_EP_RECEIVE 使能端点,从而能够接收数据

    USB_EP_HANDSHAKE 使能握手包 (ACK/NAK)的生成(仅对非同步端点)

    USB_EP_NO_INC 仅用于到另一设备 FIFO 的直接 DMA

    表 4: 外部设备数据流方向总结USB 术语

    固件栈 术语

    说明

    IN TRANSMIT 数据从设备流向主机

    OUT RECEIVE 数据从主机流向设备

    2009 Microchip Technology Inc. DS01176A_CN 第 13 页

  • AN1176

    简单的例子

    下面示例代码段说明如何初始化端点配置表,初始化的方法与例 4 所示描述符表是一致的 (见 “ 实现 USB 描述符表”)。在描述符表中向主机报告的信息要与用来配置硬件的端点配置表中提供的信息相匹配,这非常重要。

    例 7: 简单的端点配置表

    复杂的例子

    更复杂的设备可能有多种配置,或在一种配置内有多个接口。例 8 给出的端点配置表可以用于有两种配置的设备。配置 1 有两个接口 (0 和 1)。每个接口有两个端点,一个发送数据,一个接收数据。配置 2 只有一个接口,但接口有两个端点,同样,一个用来发送数据,一个接收数据。

    const EP_CONFIG gEpConfigTable[] ={ { // EP1 - 输入或输出 EP_MAX_PKT_INTR_FS, // 端点的最大包大小 USB_EP_TRANSMIT | // 端点的配置标志 USB_EP_RECEIV

    USB_EP_HANDSHAKE, USBGEN_EP_NUM, // 端点编号 USBGEN_CONFIG_NUM, // 配置编号 USBGEN_INTF_NUM, // 接口编号 0, // 备用接口设置 0 // 设备功能表中的索引 (见下面) }

    DS01176A_CN 第 14 页 2009 Microchip Technology Inc.

  • AN1176

    例 8: 复杂的端点配置表const EP_CONFIG gEpConfigTable[] ={ // 设备配置 1 端点配置 { 64, //端点的最大包大小 USB_EP_TRANSMIT| //端点的配置标志 USB_EP_HANDSHAKE, 1, // 端点编号 1, // 配置编号 (从 1开始) 0, // 接口编号 0, // 备用接口设置 (默认 =0) 0 // 设备功能表中的索引 }, { 64, // 端点的最大包大小 USB_EP_RECEIVE| // 端点的配置标志 USB_EP_HANDSHAKE, 2, // 端点编号 1, // 配置编号 (从 1开始) 0, // 接口编号 0, // 备用接口设置 (默认 =0) 0 // 设备功能表中的索引 }, { 64, //端点的最大包大小 USB_EP_TRANSMIT| //端点的配置标志 USB_EP_HANDSHAKE, 3, // 端点编号 1, // 配置编号 (从 1开始) 1, // 接口编号 0, // 备用接口设置 (默认 =0) 0 // 设备功能表中的索引 },{ 64, // 端点的最大包大小 USB_EP_RECEIVE| // 端点的配置标志 USB_EP_HANDSHAKE, 4, // 端点编号 1, // 配置编号 (从 1开始) 1, // 接口编号 0, // 备用接口设置 (默认 =0) 0 // 设备功能表中的索引 }, // 设备配置 2端点配置 { 64, //端点的最大包大小 USB_EP_TRANSMIT| //端点的配置标志 USB_EP_HANDSHAKE, 1, // 端点编号 2, // 配置编号 (从 1开始) 0, // 接口编号 0, // 备用接口设置 (默认 =0) 0 // 设备功能表中的索引 }, { 64, // 端点的最大包大小 USB_EP_RECEIVE| // 端点的配置标志 USB_EP_HANDSHAKE, 2, // 端点编号 2, // 配置编号 (从 1开始) 0, // 接口编号 0, // 备用接口设置 (默认 =0) 0 // 设备功能表中的索引 }};

    2009 Microchip Technology Inc. DS01176A_CN 第 15 页

  • AN1176

    实现支持的功能驱动程序表

    由于一个设备可以实现多个类或供应商特定的 USB 功能,Microchip PIC32 USB 固件栈使用一张表来管理对所支持的功能驱动程序的访问。表中的每一项都含有管理某一个功能驱动程序所必需的信息。如果一个设备仅实现一种 USB 功能,则表中只有一项。下面的数据结构定义了功能驱动程序表中的项。

    图 4: 功能驱动程序表表项

    Initialize字段存放的指针指向功能驱动程序的初始化例程。 EventHandler 字段存放的指针指向功能驱动程序的例程,用来处理类或供应商特定的 USB 事件。flags字段中存放的是任何驱动程序特定的标志,标志将被传递给初始化例程。关于这些例程的用途和实现方法的细节,请参阅 “ 实现 USB 功能驱动程序 ”。

    对于 USB 协议栈管理功能驱动程序而言,所需的全部数据就是这三个字段中的数据。一旦主机已经配置好设备,表项使得 USB 协议栈能够动态地选择调用的功能驱动程序。使用常规方法,功能驱动程序自身即可直接链接到 USBDEVTransferData 和 USBDEVGetLas-tError例程,因为 USB 协议栈中每个这样的例程仅有一个实现。例 9 给出了一个样本实现。

    例 9: 功能驱动程序表

    struct _function_driver_table_entry{ USBDEV_INIT_FUNCTION_DRIVER Initialize; // 初始化程序 USB_EVENT_HANDLER EventHandler; // 事件处理例程 BYTE flags; // 初始化标志};

    const FUNC_DRV gDevFuncTable[2] = { { // 通用功能驱动程序 USBGenInitialize, // 初始化例程 USBGenEventHandler, // 事件处理例程 2 // 端点编号 (低 4位) }, { // HID 功能驱动程序 USBHIDInitialize, // 初始化例程 USBHIDEventHandler, // 事件处理例程 0 // 无支持的标志 }};

    DS01176A_CN 第 16 页 2009 Microchip Technology Inc.

  • AN1176

    除了这个表之外,应用还必须实现一个例程或宏,以便提供表的基地址。例 10 给出了一种可能的实现。

    例程仅需提供一个指向表基地址的指针。并不需要表中表项个数信息,因为端点配置表给出了功能驱动程序表中每个可能表项的索引 (见 “ 实现端点配置表 ”)。

    例 10: 获取功能驱动程序表例程

    配置 USB 协议栈选项 本节将着重说明几个重要的配置选项,它们是确保 USB设备栈正常工作必需的选项。

    首先,要确保 USB 协议栈的编译是针对 Peripheral-Device-Only (仅对外部设备)模式的,一定要定义USB_SUPPORT_DEVICE宏。否则,USB 协议栈的行为将不适用于 USB 外部设备应用。其次,要确保 USB 协议栈分配的 RAM 不超出实际需要,一定要正确地定义以下宏:

    直接影响 RAM 使用的宏:

    • USB_DEV_HIGHEST_EP_NUMBER• USB_MAX_NUM_PIPES• USB_DEV_EP0_MAX_PACKET_SIZE• USB_DEV_SUPPORTS_ALT_INTERFACES

    这些宏的前三个必须根据设备应用的需要,定义成合适的整数。分配 RAM 来跟踪每个所用端点的状态信息,从端点 0 一直到使用的编号最高的端点。因此,节约RAM 的一种途径,是从最低可用编号开始分配所需端点。要向 USB 协议栈指出这种要求,应把宏USB_DEV_HIGHEST_EP_NUMBER 定义为这个最低编号。

    端点 0 能够支持的缓冲区大小是 8、 16、 32 或 64 字节。此缓冲区 RAM 的分配是基于宏USB_DEV_EP0_MAX_PACKET_SIZE的定义。必须把这个宏定义为这些值的其中一个。

    如 果 需 要 支 持 备 用 接 口,则 必 须 定 义 宏USB_DEV_SUPPORTS_ALT_INTERFACES。否 则,

    USB协议栈将不支持使用具有备用设置的USB接口。这种支持并不总是必需的,包含这种支持的话,将使用更多的RAM和闪存(见 “PIC® MCU存储器资源需求 ”)。第三,为了确保 USB 协议栈能够调用 “ 实现应用特定的 USB 支持 ” 中所述的三个用户定义的例程,必须定义宏 USB_DEV_GET_DESCRIPTOR_FUNC、USB_DEV_GET_EP_CONFIG_TABLE_FUNC 和USB_DEV_GET_FUNCTION_DRIVER_TABLE_FUNC ,使得宏值与它们关联的例程名相同。下面的示例说明:使用 “ 实现应用特定的 USB 支持 ” 所示的例子,如何定义这些宏。

    例 11: 功能标识宏定义

    const FUNC_DRV *USBDEVGetFunctionDriverTable ( void ){ return gDevFuncTable;}

    #define USB_DEV_GET_DESCRIPTOR_FUNC USBDEVGetDescriptor #define USB_DEV_GET_EP_CONFIG_TABLE_FUNC USBDEVGetEpConfigurationTable #define USB_DEV_GET_FUNCTION_DRIVER_TABLE_FUNC USBDEVGetFunctionDriverTable

    注: 关于配置选项的更多细节,参阅附录 A:USB 固件栈配置。

    2009 Microchip Technology Inc. DS01176A_CN 第 17 页

  • AN1176

    结论

    对于开发人员而言,有了 Microchip PIC32 USB 固件栈,管理 USB 设备标识、配置、控制和数据传输变得容易了。协议栈简化了对几乎是任何数量的配置或接口的支持。最重要的是,它提供了简单的功能驱动程序接口,使得设计单功能或多功能设备变得更加容易。

    参考文献

    • “Universal Serial Bus Specification, Revision 2.0” http://www.usb.org/developers/docs

    • “OTG Supplement, Revision 1.3”http://www.usb.org/developers/onthego

    • Microchip MPLAB® IDE 集成开发环境,许可证免费,下载地址:www.microchip.com/mplabide

    • Microchip 应用笔记 AN1163,《嵌入式设备上的 USB HID 类》

    • Microchip 应用笔记 AN1169,《嵌入式设备上的 USB 海量存储类》

    • Microchip 应用笔记 AN1164,《嵌入式设备上的 USB CDC 类》

    • Microchip 应用笔记 AN1166,《嵌入式设备上的 USB 通用功能》

    DS01176A_CN 第 18 页 2009 Microchip Technology Inc.

    http://www.usb.org/developers/docs/http://www.usb.org/developers/docs/

  • AN1176

    附录 A: USB 固件栈配置设备栈提供了若干配置选项,以便针对应用来定制协议栈。配置选项必须在文件 usb_config.h 中定义,任何 USB 应用都必须包含该文件。一旦选项有改变, 必须重新“ 干净地” 编译协议栈,从而重新编译所有相关的二进制文件。

    下面列出了设备栈的配置选项:

    • USB_SUPPORT_DEVICE

    • USB_DEV_EVENT_HANDLER

    • USB_DEV_HIGHEST_EP_NUMBER

    • USB_DEV_EP0_MAX_PACKET_SIZE

    • USB_DEV_SUPPORTS_ALT_INTERFACES

    • USB_DEV_GET_DESCRIPTOR_FUNC

    • USB_DEV_GET_EP_CONFIG_TABLE_FUNC

    • USB_DEV_GET_FUNCTION_DRIVER_TABLE_FUNC

    • USB_DEV_SELF_POWERED

    • USB_DEV_SUPPORT_REMOTE_WAKEUP

    • USB_SAFE_MODE

    2009 Microchip Technology Inc. DS01176A_CN 第 19 页

  • AN1176

    USB_SUPPORT_DEVICE

    用途: 确定要实现的应用是否支持外部设备操作。

    先决条件: 无

    有效值: 不需要给宏赋值。对宏进行定义,就足以选择应用的 USB 角色。

    默认值: 无定义

    示例 : #define USB_SUPPORT_DEVICE

    USB_DEV_EVENT_HANDLER

    用途: 标识 USB 协议栈设备支持层的总线事件处理例程的名称。设备支持层处理所有的标准请求(参见 “Universal Serial Bus Specification, Revision 2.0” 第 9 章)。宏始终定义为示例所示的那样,除非用户打算直接处理标准设备请求。

    先决条件: 无

    有效值: 宏应等于能够处理所有 USB 设备请求的例程名。

    默认值: USBDEVHandleBusEvent

    示例: #define USB_DEV_EVENT_HANDLER USBDEVHandleBusEvent

    USB_DEV_HIGHEST_EP_NUMBER

    用途: 确定应用将要使用的最高端点编号。

    先决条件: 无

    有效值: 有效值是 1 到 15 之间的任何整数。

    默认值: 无—— 必须由应用定义

    示例: #define USB_DEV_HIGHEST_EP_NUMBER15

    USB_DEV_EP0_MAX_PACKET_SIZE

    用途: 为端点 0定义允许的最大包大小。

    先决条件: 无

    有效值: 宏必须定义为 8、 16、 32 或 64 字节

    默认值: 8

    示例: #define USB_DEV_EP0_MAX_PACKET_SIZE 8

    注: 为管理数据传输,USB 设备固件栈将为每个端点使用额外的 RAM(见 “PIC® MCU存储器资源需求 ”)。

    注: USB 设备固件栈将使用的额外的 RAM,其字节数等于宏定义的值(见 “PIC® MCU存储器资源需求 ”)。

    DS01176A_CN 第 20 页 2009 Microchip Technology Inc.

  • AN1176

    USB_DEV_SUPPORTS_ALT_INTERFACES

    用途: 如果定义了这个宏, USB 设备固件栈将支持在单个配置内的备用接口。

    先决条件: 无

    有效值: 不需要给宏赋值。对宏进行定义,就足以使能备用接口支持。

    默认值: 无定义

    示例: #define USB_DEV_SUPPORTS_ALT_INTERFACES

    USB_DEV_GET_DESCRIPTOR_FUNC

    用途: 定义向 USB 固件栈提供描述符的例程名。应用必须实现这个例程。函数签名必须与usb_device.h头文件中定义的相匹配。

    先决条件: 无

    有效值: 定义的宏值必须是应用的“ 获取描述符” 例程名,以支持 USB 外部设备操作。

    默认值: 无—— 必须由应用定义

    示例: #define USB_DEV_GET_DESCRIPTOR_FUNC USBDEVGetDescriptor

    USB_DEV_GET_EP_CONFIG_TABLE_FUNC

    用途: 定义例程的名称,例程提供的指针指向端点配置表——根据需要,使用端点配置表来配置端点。函数签名必须与 usb_device.h中定义的相匹配。

    先决条件: 无

    有效值: 定义的宏值必须是应用的“ 获取端点配置表” 例程名,以支持 USB 外部设备操作。

    默认值: 无—— 必须由应用定义

    示例: #define USB_DEV_GET_EP_CONFIG_TABLE_FUNC USBDEVGetEpConfigurationTable

    USB_DEV_GET_FUNCTION_DRIVER_TABLE_FUNC

    用途: 定义例程的名称,例程提供的指针指向功能驱动程序表。函数签名必须与 usb_device.h 中定义的某一个相匹配。

    先决条件: 无

    有效值: 定义的宏值必须是应用的“ 获取功能驱动程序表” 例程名,以支持 USB 外部设备操作。

    默认值: 无—— 必须由应用定义

    示例: #define USB_DEV_GET_FUNCTION_DRIVER_TABLE_FUNC \

    USBDEVGetFunctionDriverTable

    注: 如果定义了这个宏,USB 设备固件栈将使用额外的闪存和 RAM 来管理备用接口(见“PIC® MCU 存储器资源需求 ”)。

    2009 Microchip Technology Inc. DS01176A_CN 第 21 页

  • AN1176

    USB_DEV_SELF_POWERED

    用途: 如果系统是自供电的 USB 外部设备,应定义这个宏。

    先决条件: 无

    有效值: 不需要给宏赋值。对宏进行定义,就足以在 USB 设备固件栈中使能对自供电设备的支持。

    默认值: 无定义

    示例: #define USB_DEV_SELF_POWERED

    USB_DEV_SUPPORT_REMOTE_WAKEUP

    用途: 如果系统要支持远程唤醒主机,应定义这个宏。

    先决条件: 无

    有效值: 不需要给宏赋值。对宏进行定义,就足以使能对远程唤醒的支持。

    默认值: 无定义

    示例: #define USB_DEV_SUPPORT_REMOTE_WAKEUP

    USB_SAFE_MODE

    用途: 定义这个宏,以便使能对整个 USB 固件栈进行参数和边界检查。

    先决条件: 无

    有效值: 不需要给宏赋值。对宏进行定义,就足以使能安全模式。

    默认值: 无定义

    示例: #define USB_SAFE_MODE

    注:必须与描述符中提供的信息匹配。

    注: 一旦执行了仔细的测试和调试,即可不再定义这个宏标签来删除此特性,从而提高效率。

    DS01176A_CN 第 22 页 2009 Microchip Technology Inc.

  • AN1176

    附录 B: 应用程序编程接口本节将对 USB 设备固件栈的应用程序编程接口(API)进行说明。应用使用 API 来初始化和维护 USB 固件栈。API 还提供了应用特定的配置信息,如设备描述符、端点配置和对功能驱动程序的访问例程,等等。

    这些 API 例程中,有些由 USB 固件栈实现,可直接被应用调用。其他的则通常称为 “调出例程 ”(callout)(或更准确地,calls out)。应用必须实现这些函数,从USB 固件栈将它们调 “出 ”(OUT)到应用中。为了让 USB 固件栈知道要调用哪一个例程,在配置协议栈期间,必须标识“调出例程”的函数名。请参考 “USB固件栈配置 ”中 USB_DEV_GET_DESCRIPTOR_FUNC、USB_DEV_GET_EP_CONFIG_TABLE_FUNC 和_DEV_GET_FUNCTION_DRIVER_TABLE_FUNC 宏,了解如何标识这些例程。

    表 5 总结了 API,并指出哪些例程是调入(IN)例程,哪些是调出 (OUT)例程。

    表 5: USB API 总结

    以下详细说明了上述 API 例程。

    操作 调用类型

    说明

    USBInitialize IN 初始化 USB 固件栈

    USBHandleEvents IN 标识并处理总线事件

    USB_DEV_GET_DESCRIPTOR_FUNC OUT 应用必须实现此函数,以提供指向所请求描述符的指针

    USB_DEV_GET_EP_CONFIG_TABLE_FUNC OUT 应用必须实现此函数,以提供指向端点配置表的指针

    USB_DEV_GET_FUNCTION_DRIVER_TABLE_FUNC OUT 应用必须实现此函数,以提供指向功能驱动程序表的指针

    2009 Microchip Technology Inc. DS01176A_CN 第 23 页

  • AN1176

    USB API – USBInitialize

    初始化 USB 固件栈,清除 USB 状态并尝试连接总线。

    语法

    BOOL USBInitialize (unsigned long flags)

    参数

    flags – USB 初始化标志 (保留,传递 0)

    返回值

    TRUE 如果成功FALSE 如果失败

    先决条件

    副作用

    USB 设备固件栈已经初始化,系统正等待与总线的连接。

    示例

    // 初始化 USB协议栈 if (!USBInitialize(0)) return FALSE;

    注: 此 “ 函数 ” 实际上可以当作宏来实现,根据 USB 协议栈的当前配置,宏实际上可能调用多个函数来初始化多个 USB 固件层。

    DS01176A_CN 第 24 页 2009 Microchip Technology Inc.

  • AN1176

    USB API – USBTasks

    这是主要的 USB 状态机事件 “ 泵 ” 例程。它检查可能已经发生的 USB 事件并对它们进行适当的处理。应用可以在查询循环中调用此例程,或者在响应 USB (或可能是定时器)中断时直接调用。

    语法

    void USBTasks (void)

    参数

    返回值

    先决条件

    必须已调用 USBInitialize,且返回值表明调用成功。

    副作用

    随 USB 设备固件的状态以及总线上的活动,副作用变化非常大。例程将标识已经发生的总线事件,如果例程能够处理的话,就采取适当的行动。如果 USB 设备固件不能直接处理事件,那么调用此例程将导致 “ 调出 ” 一个或多个功能驱动程序来处理类和供应商特定的事件。然后,功能驱动程序可能调出至应用,这取决于功能驱动程序的设计。

    示例

    // 主处理循环while(1){ // 检查 USB事件并 // 适当地处理它们 USBHandleEvents();

    // 处理其他 IO活动}

    注: 此 “ 函数 ” 实际上可以当做宏来实现,根据 USB 协议栈的当前配置,宏实际上可能调用多个函数,初始化多个 USB 固件层。

    2009 Microchip Technology Inc. DS01176A_CN 第 25 页

  • AN1176

    USB API – USB_DEV_GET_DESCRIPTOR_FUNC

    此例程是从 USB 固件 “ 调出 ”(call out)的例程,应用必须实现此例程。实际的例程名由应用来定义。设备层将使用 USB_DEV_GET_DESCRIPTOR_FUNC宏调用它(见 “USB 固件栈配置 ”),宏定义必须是例程的实际名称。在响应来自主机的 GET_DESCRIPTOR 请求时,将调用此例程。例程必须提供一个指向所请求描述符的指针。

    语法

    const void * USB_DEV_GET_DESCRIPTOR_FUNC (BYTE type, BYTE index, unsigned int *length)

    其中 USB_DEV_GET_DESCRIPTOR_FUNC是应用定义的函数名。

    参数

    type – 标识所请求描述符的类型

    index – 期望描述符的索引

    length – 指针,指向将接收所请求描述符长度的变量

    返回值

    返回指向所请求描述符的指针

    先决条件

    必须已调用 USBInitialize,且返回值表明调用成功。

    副作用

    示例

    // 这是示例实现。应用不应调用本例程const void *USBDEVGetDescriptor (BYTE type, BYTE index, unsigned int *length){ switch (type) { case USB_DESCRIPTOR_DEVICE: // 设备描述符 *length = sizeof(dev_desc); return &dev_desc;

    case USB_DESCRIPTOR_CONFIGURATION:// 配置描述符 *length = sizeof(conf_desc); return &conf_desc;

    // 处理其他描述符(如果需要的话) } return NULL;}

    DS01176A_CN 第 26 页 2009 Microchip Technology Inc.

  • AN1176

    USB API – USB_DEV_GET_EP_CONFIG_TABLE_FUNC

    此例程是从 USB 固件层“ 调出” 的例程,应用必须实现此例程。由于例程名是由应用定义的, USB 固件将使用USB_DEV_GET_EP_CONFIG_TABLE_FUNC宏调用它(见 “USB 固件栈配置 ”),宏定义必须是例程的实际名称。在设备枚举期间,将调用此函数,查找合适的端点配置。例程必须提供一个指向端点配置表的指针。

    语法

    const EP_CONFIG * USB_DEV_GET_EP_CONFIG_TABLE_FUNC (int *length)

    其中 USB_DEV_GET_EP_CONFIG_TABLE_FUNC是应用定义的函数名。

    参数

    length – 指针,指向将接收端点配置表中有多少个表项的整型变量。

    返回值

    例程必须返回指向端点配置表第一个表项的指针。

    先决条件

    必须已调用 USBInitialize,且返回值表明调用成功。

    副作用

    示例

    // 这是示例实现。应用不应调用本例程const EP_CONFIG *USBDEVGetEpConfigurationTable (int *num_entries){ // 提供表项的数目 *num_entries = sizeof(gEpConfigTable)/sizeof(EP_CONFIG);

    // 提供表指针 return gEpConfigTable;

    }

    2009 Microchip Technology Inc. DS01176A_CN 第 27 页

  • AN1176

    USB API – USB_DEV_GET_FUNCTION_DRIVER_TABLE_FUNC

    此例程是从 USB 固件层“ 调出” 的例程,应用必须实现此例程。实际的例程名是由应用定义的, USB 固件将使用 USB_DEV_GET_FUNCTION_DRIVER_TABLE_FUNC宏调用它(见 “USB 固件栈配置 ”),宏定义必须是例程的实际名称。当主机配置设备时,将调用此函数,以访问功能驱动程序表。

    语法

    const FUNC_DRV * USB_DEV_GET_FUNCTION_DRIVER_TABLE_FUNC (void)

    其中 USB_DEV_GET_FUNCTION_DRIVER_TABLE_FUNC是应用定义的函数名。

    参数

    返回值

    例程必须返回指向功能驱动程序表第一个表项的指针。

    先决条件

    必须已调用 USBInitialize,且返回值表明调用成功。

    副作用

    示例

    // 这是示例实现。应用不应调用本例程const FUNC_DRV *USBDEVGetFunctionDriverTable (void){ // 指向数组的索引,提供接口指针 return gDevFuncTable;

    }

    DS01176A_CN 第 28 页 2009 Microchip Technology Inc.

  • AN1176

    附录 C: USB 功能驱动程序接口 本节将说明 USB 固件栈中类或供应商特定的功能驱动程序接口(FDI)。 FDI 为一个或多个类特定的 USB“函数”提供了与主机通信的方法。这包括在适当的时候对函数进行初始化、提供数据传输能力以及提供接收事件的方法。

    由于当主机选择设备配置时才会被初始化函数,由于USB事件本质上是异步的,会从USB固件栈“调出”一些 FDI 函数。其他的则被调入 USB 固件栈。表 6 总结了 FDI,并指出哪些是从 USB 固件栈调入的,哪些是调出的。

    以下详细说明了上述 USB FDI 例程。

    表 6: USB 设备层接口总结

    操作调用 类型

    说明

    USBDEVTransferData IN 开始数据传输(发送或接收)

    USBDEVGetLastError IN 提供关于总线错误的信息

    (通过功能驱动程序表中的指针调用此例程)

    OUT 由功能驱动程序实现。当设备配置了此函数时,通过设备层调用

    (通过功能驱动程序表中的指针调用此例程)

    OUT 由功能驱动程序实现。处理所有功能特定的总线事件以及类或供应商请求

    2009 Microchip Technology Inc. DS01176A_CN 第 29 页

  • AN1176

    USB FDI - USBDEVTransferData

    例程在给定方向上从给定端点发起 USB 数据传输。调用程序提供缓冲区,用来在发送时存放要传输的数据,在接收时存放接收的数据。

    语法

    BOOL USBDEVTransferData ( TRANSFER_FLAGS flags, void *buffer, unsigned int size )

    参数

    flags – 用于指示端点和方向

    buffer – 指针,指向要存放发送或接收数据的缓冲区

    size – 要传输的数据字节数

    返回值

    TRUE 如果成功开始了数据传输

    FALSE 如果没有成功

    前提条件

    必须已经成功调用了 USBInitialize,系统必须已与 USB 上的主机连接。

    副作用

    已经准备好进行 USB 数据传输。当主机请求时,实际的数据传输将稍后在 USB 控制器的控制下经过 DMA 进行。数据传输完成后,将发送一个 EVENT_TRANSFER 事件给功能驱动程序的 (功能事件处理例程)例程。

    示例

    switch(bRequest){case SET_LINE_CODING:

    // 在 EP0上开始 Rx事务,以便获取线路编码gCdcSer.flags |= CDC_FLAGS_LINE_CTRL_BUSY;return USBDEVTransferData(XFLAGS(USB_EP0|USB_RECEIVE), &line_coding, sizeof(line_coding));

    // 处理其他请求}

    DS01176A_CN 第 30 页 2009 Microchip Technology Inc.

  • AN1176

    USB FDI – USBDEVGetLastError

    例程提供最新错误状态的位映射表示。

    语法

    unsigned long USBDEVGetLastError ( void )

    参数

    返回值

    最新错误状态的位映射表示 *:

    USBDEV_PID_ERR 包的包 ID 字段错误

    USBDEV_CRC16 数据包中有一个 CRC 错误

    USBDEV_DFN8 数据包的数据字段大小不是 8 位的整数倍

    USBDEV_BTO_ERR 已发生总线翻转超时错误 (turn-around time-out error)

    USBDEV_DMA_ERR DMA 引擎不能读 /写存储器

    USBDEV_BTS_ERR 位填充错误

    USBDEV_XFER_ID HAL 无法标识给定的传输 EP

    USBDEV_NO_EP 给定的是无效的端点编号

    USBDEV_DMA_ERR2 在传输过程中,出现尝试发起 DMA 事务的错误

    * 如果自上次调用 USBDEVGetLastError以来,发生了不止一个的错误,这些值可以一起进行“或(OR)”运算。

    先决条件

    已经成功调用了 USBInitialize。

    副作用

    错误的内部记录已被清除。然而,还没有执行任何动作来修复错误状态。调用程序可能需要采取适当的步骤。

    示例

    switch(event){case EVT_BUS_ERR: error = USBDEVGetLastError(); HandleBusError(error);// 检查每个错误并进行适当处理的例程

    break;// 处理其他事件 ...}

    2009 Microchip Technology Inc. DS01176A_CN 第 31 页

  • AN1176

    USB FDI –

    此例程是从 USB 固件“ 调出”(call out)的例程,必须由 USB 外部设备功能驱动程序实现。实际名称由驱动程序定义。USB 固件将使用功能驱动程序表中的指针调用它。例程必须对实现它的功能驱动程序进行基本初始化。在向功能驱动程序发送任何事件之前,当主机设置配置时, USB 固件将调用此例程。

    语法

    BOOL (unsigned long flags)

    其中 (功能驱动程序表例程)是驱动程序定义的函数名。

    参数

    flags – 功能驱动程序的初始化标志 (驱动程序特定的)。

    返回值

    TRUE 如果成功,

    FALSE 如果失败

    先决条件

    必须已调用 USBInitialize,且返回值表明调用成功。

    副作用

    副作用取决于实现这个例程的功能驱动程序。然而,在所有情形下,都必须初始化驱动程序并准备接收类和 /或供应商特定的事件。

    示例

    // 这是示例实现。应用不应调用本函数BOOL USBUARTInit (unsigned long flags){ memset(&gCdcSer, 0, sizeof(gCdcSer)); // 初始化需要的状态 gCdcSer.flags = flags & CDC_FLAGS_INIT_MASK; // 根据需要,进行其他的初始化工作 return TRUE;}

    // 示例功能驱动程序表表项:

    // USB CDC串行仿真功能驱动程序{ USBUARTInit,// 初始化例程 USBUARTEventHandler,//事件例程 0 // 初始化标志}, //可以跟更多的表项

    DS01176A_CN 第 32 页 2009 Microchip Technology Inc.

  • AN1176

    USB FDI –

    此例程是从 USB 固件 “ 调出 ” 的例程,必须由 USB 外部设备功能驱动程序实现。实际名称由驱动程序定义。USB 固件将使用功能驱动程序表中的指针调用它。

    语法

    BOOL (USB_EVENT event, void *data, int size)

    其中 < Func-Driver Event Handling Routine >(功能驱动程序事件处理例程)是驱动程序定义的函数名。

    参数

    event – 枚举数据类型,标识已发生的事件 (见 “ 预定义事件 ”)data – 指针,指向事件相关数据 (如果有数据的话,见 “ 预定义事件 ”)size – 事件相关数据的大小,以字节为单位。

    返回值

    TRUE 如果成功

    FALSE 如果失败 (或者,如果没有完成事件的处理)

    先决条件

    必须已调用 USBInitialize,且返回值表明调用成功。

    副作用

    副作用取决于实现这个例程的功能驱动程序。然而,在所有情形下,驱动程序都必须恰当地处理事件,并准备好处理预计将要发生的下一事件 (例如,如果合适的话,将开始新的数据传输)。

    示例

    有关如何实现此函数的示例,请参见 “ 事件处理 ”。

    预定义事件

    EVENT_NONE 错误的事件触发 (应从不发生)。

    EVENT_TRANSFER 前一个 USB 传输已经以完成。此事件提供了指向以下数据的指针: typedef struct _transfer_event_data{ UINT32 size; // 传输的实际字节数 USB_XFER_DIR direction; // 端点方向 BYTE ep_num; // 端点编号} USB_TRANSFER_EVENT_DATA;

    EVENT_SOF 帧的开头已经出现。

    EVENT_RESUME 总线上已经接收到一个继续信号。

    EVENT_SUSPEND 总线上已经出现一个挂起信号 (3 ms 空闲)。

    EVENT_RESET 总线上接收到一个复位信号。

    EVENT_DETACH 已断开 USB 电缆的连接。

    EVENT_ATTACH 已连接 USB 电缆。

    EVENT_STALL 一个或多个端点上出现停止。此事件提供一个指向 16 位字的指针,字提供了哪个端点已停止的位图 (位 0 = EP0 停止, 位 1 = EP1 停止,等等)。

    EVENT_SETUP 已经接收到设备或功能特定的设置包。此事件提供了指向设置包数据的指针:

    typedef struct SetupPkt{ union // 偏移量 说明 { // ------ -------------------------- BYTE bmRequestType; // 0 请求类型的位图

    2009 Microchip Technology Inc. DS01176A_CN 第 33 页

  • AN1176

    struct { BYTE recipient: 5; // 请求的接收者 BYTE type: 2; // 请求的类型 BYTE direction: 1; // 数据传输的方向 }; }requestInfo; BYTE bRequest; // 1 请求类型 UINT16 wValue; // 2 取决于 bRequest UINT16 wIndex; // 4 取决于 bRequest UINT16 wLength; // 6 取决于 bRequest} SETUP_PKT;

    EVENT_USER_BASE 对于应用定义而言,这是第一个可用的事件。把这个基数加上整数值以定义应用特定的事件。

    EVENT_BUS_ERROR 总线上出现一个错误。调用 USBHALGetLastError()来标识错误并清除错误记录。

    DS01176A_CN 第 34 页 2009 Microchip Technology Inc.

  • AN1176

    软件许可协议

    Microchip Technology Incorporated (以下简称 “ 本公司 ”)在此提供的软件旨在向本公司客户提供专门用于 Microchip 生产的产品的软件。

    本软件为本公司及 / 或其供应商所有,并受到适用的版权法保护。版权所有。使用时违反前述约束的用户可能会依法受到刑事制裁,并可能由于违背本许可的条款和条件而承担民事责任。

    本软件是按 “ 现状 ” 提供的。任何形式的保证,无论是明示的、暗示的或法定的,包括但不限于有关适销性和特定用途的暗示保证,均不适用于本软件。对于在任何情况下、因任何原因造成的特殊的、附带的或间接的损害,本公司概不负责。

    附录 D: USB 设备栈编程指南的源代码Microchip USB 设备栈固件的源代码是在免费许可证协议的基础上提供。它以一个档案文件的形式提供,可以从 Microchip 公司网站下载,地址是:

    www.microchip.com.下载档案文件之后,应查阅版本说明,了解当前的版本号以及软件的修改历史。

    2009 Microchip Technology Inc. DS01176A_CN 第 35 页

  • AN1176

    版本历史

    版本 A (2008 年 2 月)这是本文档的初始版本。

    DS01176A_CN 第 36 页 2009 Microchip Technology Inc.

  • 请注意以下有关 Microchip 器件代码保护功能的要点:

    • Microchip 的产品均达到 Microchip 数据手册中所述的技术指标。

    • Microchip 确信:在正常使用的情况下, Microchip 系列产品是当今市场上同类产品中最安全的产品之一。

    • 目前,仍存在着恶意、甚至是非法破坏代码保护功能的行为。就我们所知,所有这些行为都不是以 Microchip 数据手册中规定的操作规范来使用 Microchip 产品的。这样做的人极可能侵犯了知识产权。

    • Microchip 愿与那些注重代码完整性的客户合作。

    • Microchip 或任何其他半导体厂商均无法保证其代码的安全性。代码保护并不意味着我们保证产品是“ 牢不可破 ”的。

    代码保护功能处于持续发展中。 Microchip 承诺将不断改进产品的代码保护功能。任何试图破坏 Microchip 代码保护功能的行为均可视为违反了《数字器件千年版权法案(Digital Millennium Copyright Act)》。如果这种行为导致他人在未经授权的情况下,能访问您的软件或其他受版权保护的成果,您有权依据该法案提起诉讼,从而制止这种行为。

    提供本文档的中文版本仅为了便于理解。请勿忽视文档中包含

    的英文部分,因为其中提供了有关 Microchip 产品性能和使用情况的有用信息。Microchip Technology Inc. 及其分公司和相关公司、各级主管与员工及事务代理机构对译文中可能存在的任何差错不承担任何责任。建议参考 Microchip TechnologyInc. 的英文原版文档。

    本出版物中所述的器件应用信息及其他类似内容仅为您提供便

    利,它们可能由更新之信息所替代。确保应用符合技术规范,是您自身应负的责任。Microchip 对这些信息不作任何明示或暗示、书面或口头、法定或其他形式的声明或担保,包括但不

    限于针对其使用情况、质量、性能、适销性或特定用途的适用性的声明或担保。 Microchip 对因这些信息及使用这些信息而引起的后果不承担任何责任。如果将 Microchip 器件用于生命维持和 / 或生命安全应用,一切风险由买方自负。买方同意在

    由此引发任何一切伤害、索赔、诉讼或费用时,会维护和保障Microchip 免于承担法律责任,并加以赔偿。在 Microchip 知识产权保护下,不得暗中或以其他方式转让任何许可证。

    2009 Microchip Technology Inc.

    商标

    Microchip 的名称和徽标组合、 Microchip 徽标、 Accuron、dsPIC、 KEELOQ、 KEELOQ 徽标、 MPLAB、 PIC、PICmicro、 PICSTART、 rfPIC、 SmartShun 和 UNI/O 均为Microchip Technology Inc. 在美国和其他国家或地区的注册商标。

    FilterLab、 Hampshire、 Linear Active Thermistor、MXDEV、 MXLAB、 SEEVAL、 SmartSensor 和 The Embedded Control Solutions Company 均为 Microchip Technology Inc. 在美国的注册商标。

    Analog-for-the-Digital Age、 Application Maestro、CodeGuard、 dsPICDEM、 dsPICDEM.net、 dsPICworks、dsSPEAK、 ECAN、 ECONOMONITOR、 FanSense、In-Circuit Serial Programming、 ICSP、 ICEPIC、 Mindi、MiWi、MPASM、MPLAB Certified 徽标、MPLIB、MPLINK、mTouch、nanoWatt XLP、PICkit、PICDEM、PICDEM.net、PICtail、PIC32 徽标、PowerCal、PowerInfo、PowerMate、PowerTool、 REAL ICE、 rfLAB、 Select Mode、 Total Endurance、TSHARC、WiperLock 和 ZENA 均为 Microchip Technology Inc. 在美国和其他国家或地区的商标。

    SQTP 是 Microchip Technology Inc. 在美国的服务标记。

    在此提及的所有其他商标均为各持有公司所有。

    © 2009, Microchip Technology Inc. 版权所有。

    DS01176A_CN 第 ii 页

    Microchip 位于美国亚利桑那州 Chandler 和 Tempe 与位于俄勒冈州Gresham 的全球总部、设计和晶圆生产厂及位于美国加利福尼亚州和印度的设计中心均通过了 ISO/TS-16949:2002 认证。公司在 PIC® MCU 与 dsPIC® DSC、KEELOQ? 跳码器件、串行 EEPROM、单片机外设、非易失性存储器和模拟产品方面的质量体系流程均符合 ISO/TS-16949:2002。此外, Microchip 在开发系统的设计和生产方面的质量体系也已通过了 ISO 9001:2000 认证。

  • DS01176A_CN 第 38 页 2009 Microchip Technology Inc.

    美洲公司总部 Corporate Office2355 West Chandler Blvd.Chandler, AZ 85224-6199Tel: 1-480-792-7200 Fax: 1-480-792-7277技术支持:http://support.microchip.com网址:www.microchip.com亚特兰大 AtlantaDuluth, GA Tel: 678-957-9614 Fax: 678-957-1455波士顿 BostonWestborough, MA Tel: 1-774-760-0087 Fax: 1-774-760-0088芝加哥 ChicagoItasca, IL Tel: 1-630-285-0071 Fax: 1-630-285-0075克里夫兰 ClevelandIndependence, OH Tel: 216-447-0464 Fax: 216-447-0643达拉斯 DallasAddison, TX Tel: 1-972-818-7423 Fax: 1-972-818-2924底特律 DetroitFarmington Hills, MI Tel: 1-248-538-2250Fax: 1-248-538-2260科科莫 KokomoKokomo, IN Tel: 1-765-864-8360Fax: 1-765-864-8387洛杉矶 Los AngelesMission Viejo, CA Tel: 1-949-462-9523 Fax: 1-949-462-9608

    圣克拉拉 Santa ClaraSanta Clara, CA Tel: 408-961-6444Fax: 408-961-6445加拿大多伦多 TorontoMississauga, Ontario, CanadaTel: 1-905-673-0699 Fax: 1-905-673-6509

    亚太地区

    亚太总部 Asia Pacific OfficeSuites 3707-14, 37th FloorTower 6, The GatewayHarbour City, KowloonHong KongTel: 852-2401-1200Fax: 852-2401-3431中国 - 北京Tel: 86-10-8528-2100 Fax: 86-10-8528-2104

    中国 - 成都Tel: 86-28-8665-5511Fax: 86-28-8665-7889

    中国 - 香港特别行政区Tel: 852-2401-1200 Fax: 852-2401-3431

    中国 - 南京Tel: 86-25-8473-2460Fax: 86-25-8473-2470

    中国 - 青岛Tel: 86-532-8502-7355 Fax: 86-532-8502-7205

    中国 - 上海Tel: 86-21-5407-5533 Fax: 86-21-5407-5066

    中国 - 沈阳Tel: 86-24-2334-2829 Fax: 86-24-2334-2393

    中国 - 深圳Tel: 86-755-8203-2660 Fax: 86-755-8203-1760

    中国 - 武汉Tel: 86-27-5980-5300 Fax: 86-27-5980-5118

    中国 - 厦门Tel: 86-592-238-8138 Fax: 86-592-238-8130中国 - 西安Tel: 86-29-8833-7252 Fax: 86-29-8833-7256

    中国 - 珠海Tel: 86-756-321-0040 Fax: 86-756-321-0049

    台湾地区 - 高雄Tel: 886-3-6578-300Fax: 886-3-6578-370

    台湾地区 - 台北Tel: 886-2-2500-6610 Fax: 886-2-2508-0102

    台湾地区 - 新竹Tel: 886-3-572-9526Fax: 886-3-572-6459

    亚太地区

    澳大利亚 Australia - SydneyTel: 61-2-9868-6733 Fax: 61-2-9868-6755

    印度 India - BangaloreTel: 91-80-3090-4444 Fax: 91-80-3090-4080

    印度 India - New DelhiTel: 91-11-4160-8631Fax: 91-11-4160-8632

    印度 India - PuneTel: 91-20-2566-1512Fax: 91-20-2566-1513

    日本 Japan - YokohamaTel: 81-45-471- 6166 Fax: 81-45-471-6122韩国 Korea - DaeguTel: 82-53-744-4301Fax: 82-53-744-4302

    韩国 Korea - SeoulTel: 82-2-554-7200 Fax: 82-2-558-5932 或82-2-558-5934

    马来西亚 Malaysia - KualaLumpurTel: 60-3-6201-9857Fax: 60-3-6201-9859

    马来西亚 Malaysia - PenangTel: 60-4-227-8870Fax: 60-4-227-4068

    菲律宾 Philippines - ManilaTel: 63-2-634-9065Fax: 63-2-634-9069新加坡 SingaporeTel: 65-6334-8870 Fax: 65-6334-8850

    泰国 Thailand - BangkokTel: 66-2-694-1351Fax: 66-2-694-1350

    欧洲奥地利 Austria - WelsTel: 43-7242-2244-39Fax: 43-7242-2244-393丹麦 Denmark-CopenhagenTel: 45-4450-2828 Fax: 45-4485-2829法国 France - ParisTel: 33-1-69-53-63-20 Fax: 33-1-69-30-90-79德国 Germany - MunichTel: 49-89-627-144-0 Fax: 49-89-627-144-44意大利 Italy - Milan Tel: 39-0331-742611 Fax: 39-0331-466781荷兰 Netherlands - DrunenTel: 31-416-690399 Fax: 31-416-690340西班牙 Spain - MadridTel: 34-91-708-08-90Fax: 34-91-708-08-91英国 UK - WokinghamTel: 44-118-921-5869Fax: 44-118-921-5820

    全球销售及服务网点

    03/26/09

    引言假设功能与特性限制系统硬件PICR MCU存储器资源需求PICR MCU硬件资源需求表 1: PIC® MCU I/O引脚用途

    安装源文件源文件构成表 2: 源文件

    演示应用协议栈架构图 1: 概念化的协议栈

    创建USB应用例 1: 主应用程序逻辑例 2: 初始化例程例 3: 事件处理例程图 2: 端点配置表和功能驱动程序表例 4: 描述符表定义例 5: 描述符表初始化例 6: 获取描述符例程及支持代码图 3: 端点配置表结构表 3: 端点配置标志表 4: 外部设备数据流方向总结例 7: 简单的端点配置表例 8: 复杂的端点配置表图 4: 功能驱动程序表表项例 9: 功能驱动程序表例 10: 获取功能驱动程序表例程例 11: 功能标识宏定义

    结论参考文献附录 A: USB固件栈配置附录 B: 应用程序编程接口表 5: USB API总结

    附录 C: USB功能驱动程序接口表 6: USB设备层接口总结

    附录 D: USB设备栈编程指南的源代码版本历史全球销售及服务网点