STM32Cube的USB_device软件库说明

一、STM32F1 的 USB 特性

  STM32F1 的 USB 外设实现了 USB2.0 的接口和 APB1 总线间的接口。它有以下特性:

  • 符合 USB2.0 全速设备的技术规范
  • 可配置 1 到 8 个 USB 端点
  • CRC(循环冗余校验)生成/校验,反向不归零(NRZI)编码/解码和位填充
  • 支持同步传输
  • 支持批量/同步端点的双缓冲区机制
  • 支持 USB 挂起/恢复操作
  • 帧锁定时钟脉冲生成

  STM32F1的 USB 外设使用标准的 48Mhz 时钟,允许每个端点有独立的缓冲区,每个端点最大为 512 字节缓冲,最大 16 个单向或 8 个双向端点。USB 的传输格式由硬件完成,状态可以由寄存器标记,可以很大程度上简化我们的程序设计。USB模块启动时间tSTARTUP最大为1us, 这个需要在编程时注意。
在这里插入图片描述
  ST 官方 Cube 库中提供的 官方 USB 协议栈,主要是包含了 USBD 内核USB 各种类。USBD内核 一般是固定的,用户一般不需要修改,但 USBD类,如果用户需要修改或者扩展,比如复合设备或者用户自定义设备,则需要用户自行修改。
  USB 协议栈将所有 USB 类都抽象成一个数据结构:USBD_ClassTypeDef,USBD 内核与USBD 类之间的纽带就是 USBD_ClassType 这个结构体。这个结构体是一个抽象类,定义了一些虚拟函数,比如初始化,反初始化,类请求指令处理函数,端点 0 发送完成,端点 0 接收处理,数据发送完成,数据接收处理,SOF 中断处理,同步传输发送未完成,同步传输接收未完成处理等等;用户在实现自己具体的 USB 类的时候需要将它实例化,USBD_ClassTypeDef 结构体是 USBD 内核提供给外部定义一个 USB 设备类的窗口,而 USB 类文件实际就是实现这个结构体具体实例化的过程。最后将这个具体实例化的对象注册到 USBD 内核的同时,USBD 内核 与 USBD 类也进行了关联。

二、USB 设备库

USB 设备库:

• 支持多包传输特性:不需按最大包尺寸划分,即可发送大量数据。
• 支持控制端点上最多 3 个双向传输 (兼容 OHCI 控制器)。
• 无需更改库代码 (只读),使用配置文件更改内核和库配置。
• 包括 32 位对齐数据结构体以处理高速模式中基于 DMA 的传输。
• 支持用户级别的多 USB OTG 内核实例 (配置文件)。

注 :   USB 设备库可与 RTOS 共用或单独使用;CMSIS RTOS 封装的作用是对 OS 内核抽象。
   USB 设备样例不显示消息。
在这里插入图片描述

三、USB 设备库架构

USB 设备库主要分为三层,应用在这三层之上。

第一层主要包含两部分:内核类驱动

内核包含四个主要模块:

  1. USB 内核模块:提供本级 API、管理内部 USB 设备库状态机、处理 USB 中断的回调
  2. USB 请求模块:处理 USB 标准协议中第 9 章所规定的请求
  3. USB I/O 请求模块:处理底层 I/O 请求
  4. USB 日志和调试模块:遵循调试级别 USB_DEBUG_LEVEL,输出用户、日志、错误和调试消息。

• USB 设备库包括一组预定义的类驱动,可通过 USBD_RegisterClass () 程序链接至 USB内核。

  USB 设备库为兼容 USB 2.0 的通用 USB 设备栈,与所有 STM32 USB 内核兼容,由于配置封装文件解除了 USB 库与底层驱动的依赖关系,因此它可轻松链接至任何 USB HAL 驱动。

在这里插入图片描述
在这里插入图片描述

四、USB OTG 硬件抽象层

  可使用底层驱动将 USB OTG 内核与高层栈连接起来。
在这里插入图片描述

  • 底层(底层 USB 驱动)为设备和 OTG 模式提供了通用 API:每种模式中的内核初始化及对传输流的控制
  • 外设控制器驱动 (PCD)层提供了访问设备模式及此模式中处理中断程序的 API。
  • OTG 控制器驱动 (OTG)层提供了访问 OTG 模式及此模式中处理中断程序的 API。

五、USB 驱动编程手册

5.1 设备初始化

使用 stm32fxxx_hal_pcd.c 文件中的下述函数初始化设备:

HAL_StatusTypeDef HAL_PCD_Init(PCD_HandleTypeDef *hpcd)

5.2 端点配置

  USB 内核初始化之后,上层可能调用底层驱动,打开或关闭激活端点,开始传输数据。使用下列两个 API:

//ep_addr、 ep_mps 和 ep_type 分别为端点地址、最大数据传输和传输类型。
HAL_StatusTypeDef HAL_PCD_EP_Open(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint16_t ep_mps, uint8_t ep_type)
HAL_StatusTypeDef HAL_PCD_EP_Close(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)

5.3 设备内核结构体

  设备库中使用的主要结构体为设备句柄,其类型为 “USBD_HandleTypedef” ,在 usbd_def.h 中。
  USB 全局设备结构体包含所有变量和结构体,用以实时保存与设备、控制传输状态机以及端点的状态相关的所有信息。

/* USB Device handle structure */
typedef struct _USBD_HandleTypeDef
{
  uint8_t                 id;
  uint32_t                dev_config;  // dev_config 保存着当前 USB 设备配置
  uint32_t                dev_default_config;
  uint32_t                dev_config_status;
  USBD_SpeedTypeDef       dev_speed;
  USBD_EndpointTypeDef    ep_in[16];
  USBD_EndpointTypeDef    ep_out[16];
  uint32_t                ep0_state;  // ep0_state 控制着状态机
  uint32_t                ep0_data_len;
  uint8_t                 dev_state;  // dev_state 定义了连接、配置和上电状态
  uint8_t                 dev_old_state;
  uint8_t                 dev_address;
  uint8_t                 dev_connection_status;
  uint8_t                 dev_test_mode;
  uint32_t                dev_remote_wakeup;

  USBD_SetupReqTypedef    request;
  USBD_DescriptorsTypeDef *pDesc;
  USBD_ClassTypeDef       *pClass;
  void                    *pClassData;
  void                    *pUserData;
  void                    *pData;
} USBD_HandleTypeDef;
//在此结构体中, dev_state 定义了连接、配置和上电状态:
/*  Device Status */
#define USBD_STATE_DEFAULT                              0x01U
#define USBD_STATE_ADDRESSED                            0x02U
#define USBD_STATE_CONFIGURED                           0x03U
#define USBD_STATE_SUSPENDED                            0x04U


/*  EP0 State */
#define USBD_EP0_IDLE                                   0x00U
#define USBD_EP0_SETUP                                  0x01U
#define USBD_EP0_DATA_IN                                0x02U
#define USBD_EP0_DATA_OUT                               0x03U
#define USBD_EP0_STATUS_IN                              0x04U
#define USBD_EP0_STATUS_OUT                             0x05U
#define USBD_EP0_STALL                                  0x06U

#define USBD_EP_TYPE_CTRL                               0x00U
#define USBD_EP_TYPE_ISOC                               0x01U
#define USBD_EP_TYPE_BULK                               0x02U
#define USBD_EP_TYPE_INTR                               0x03U

5.4 USB 数据传输流程

  PCD 层提供所需的所有 API,可启动及控制传输流,见下列函数:

HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len)
HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len)
HAL_StatusTypeDef HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
HAL_StatusTypeDef HAL_PCD_EP_ClrStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
HAL_StatusTypeDef HAL_PCD_EP_Flush(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)

  PCD 层有一个函数必须被 USB 中断调用:

void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)

  stm32fxxx_hal_pcd.h 文件包含了库内核层处理 USB 事件时调用的函数原型。

六、USB 设备库概述

  USB 设备库基于通用 USB 底层驱动开发,可在全速和高速模式中工作。
  它实现了USB 2.0版本定义的USB设备库状态机。此模块功能由USB设备库固件包中"Core" 目录下的文件提供 。USB 类模块为类层,与协议规范兼容。
在这里插入图片描述

6.1 USB 设备库描述

6.1.1 USB 设备库流程

处理控制端点 0

  USB 规范定义了四种传输类型:控制传输、中断传输、批量传输和同步传输。USB 主机通过控制端点向设备发送请求(在这种情况下,控制端点为端点 0)。 请求作为 SETUP 包发送到设备。这些请求可分为三类:标准、特定类、特定厂商。因为标准请求对所有 USB 设备都是通用的,所以库可收到和处理控制端点 0上的所有标准请求。
  特定类和特定厂商请求的格式和意义并不对所有 USB 设备通用。
  所有 SETUP 请求都由处于中断模式的状态机处理。 USB 正确传输的末尾会产生一个中断。库代码收到该中断。在中断处理程序中,触发端点被识别。如果事件为端点 0 上的设置,则保存所收到设置的参数且启动状态机。

非控制端点上的事务

  特定类内核使用非控制端点,方法是通过数据 IN 和 OUT 阶段回调,调用一组函数发送或接收数据。

SETUP 包的数据结构

  当一个新的 SETUP 包到达时, SETUP 包的所有八个字节都被复制到一个内部结构体USB_SETUP_REQ req,因此在处理期间,下一个 SETUP 包不会覆盖前一个包。此内部结 构体定义为:

typedef  struct  usb_setup_req
{
  uint8_t   bmRequest;
  uint8_t   bRequest;
  uint16_t  wValue;
  uint16_t  wIndex;
  uint16_t  wLength;
} USBD_SetupReqTypedef;
标准请求

  下面 USB 规范表中的大多数请求都作为库中的标准请求处理。该表列出了所有标准请求及其库中的有效参数。此表中没有的请求则认为非标准请求。
在这里插入图片描述
在这里插入图片描述
注 : 在列状态中:D = 默认状态; A = 地址状态; C = 配置状态; All = 全部状态。EP: D0-D3 = 端点地址; D4-D6 = 保留为零; D7= 0: OUT 端点, 1:IN 端点。

非标准请求

所有非标准请求都通过回调函数传至特定类的代码。

SETUP 阶段

  库传递所有非标准请求到特定类的代码,回调为pdev->pClass->Setup (pdev, req)函数。 非标准请求包括用户解释请求和无效请求。用户解释请求为特定类请求、特定厂商请求,或库认为无效但应用欲解释为有效的请求。
  无效请求为非标准请求、非用户解释请求。因为 pdev->pClass->Setup (pdev, req) 在 SETUP 阶段后、数据阶段前调用,所以用户代码应负责在 pdev->pClass->Setup (pdev, req) 中解析 SETUP 包的内容(req)。若请求无效,则用户代码必须调用 USBD_CtlError(pdev , req),并返回 pdev->pClass->Setup (pdev, req) 的调用者对于用户解释请求,如果请求有数据阶段,用户代码应为后续数据阶段准备数据缓冲;否则用户代码执行请求,返回 pdev->pClass->Setup (pdev, req) 的调用者。

DATA 阶段

  类层使用标准 USBD_CtlSendData 和 USBD_CtlPrepareRx 发送或接收数据,数据传输流由库内部处理,用户不需要将数据切分为 ep_size 大小的包。

状态阶段

当从 pdev->pClass->Setup (pdev, req) 回调返回之后,状态阶段由库处理。
在这里插入图片描述
  如图7: USB设备库处理流程图中所示,USB编程只需要三个模块:USB库、USB类和主应用。
主应用执行用户定义程序,main.cstm32fxx_it.cusbd_conf.cusbd_desc.c其头文件为用户开发自己的应用时的主要文件(应用强制),用户可根据应用对其修改 (类驱动)

  若需初始化 USB HAL 驱动、 USB 设备库、和板级支持包 (BSP)并启动库,用户需调用这 三个 API ( USB 设备库初始化):

  • USBD_Init ():此函数初始化设备栈,加载类驱动与描述符地址。设备描述符存储于 usbd_desc.c 和 usbd_desc.h (用于配置描述符类型)文件中。
  • USBD_RegisterClass():此函数将类驱动链接到设备内核。
  • USBD_Start():此函数允许用户开启 USB 设备内核。

6.1.2 USB 设备数据流程

  因为 USB OTG 内核支持多包特性,所以在需要封装来管理控制端点上的多包特性时, USB 库 (USB 内核和 USB 类层) 通过 IO 请求层处理端点0 (EP0)上的数据处理,当其它端点被使用时,直接从 stm32fxxx_hal_pcd 层处理。下图显示了该数据流方案。
在这里插入图片描述

6.1.3 具有底层驱动的内核接口

  如前面所述,底层接口层为 STM32Cube HAL 的链接层, USB 设备库用其与STM32Cube HAL 底层驱动接口。
  底层接口实现了底层 API 函数,在 USB 事件后调用库内核回调函数。
在 STM32Cube 解决方案中,因为底层接口的一些部分依赖于板子和系统,所以底层接口的实现作为 USB 设备样例的一部分提供。
下表列出了底层 API 函数:
  注 : 这些 API 由 USB 设备配置文件 (usbd_conf.c)提供。用户应在用户文件中实现,适配到USB 设备控制器驱动。

API说明
USBD_LL_Init底层初始化
USBD_LL_DeInit底层取消初始化
USBD_LL_Start底层开始
USBH_LL_Stop底层停止
USBD_LL_OpenEP初始端点
USBD_LL_CloseEP关闭并对端点状态取消初始化
USBD_LL_FlushEP除掉底层驱动的一个端点。
USBD_LL_StallEP在底层驱动的端点上设置暂停状态。
USBD_LL_ClearStallEP在底层驱动的端点上清除暂停状态。
USBD_LL_IsStallEP返回暂停状态。
USBD_LL_SetUSBAddress为设备指定 USB 地址
USBD_LL_Transmit通过端点传输数据
USBD_LL_PrepareReceive为接收准备端点
USBD_LL_GetRxDataSize返回最后传输的包大小。

6.1.4 USB 设备库接口模型

  USB 设备库是由通用可移植的 USB 设备内核和类模块构建而成。
在这里插入图片描述

下面是在 USB 事件后,底层接口调用的设备库回调函数。

回调函数说明
HAL_PCD_ConnectCallback设备连接回调
HAL_PCD_DataInStageCallback数据 IN 阶段回调
HAL_PCD_DataOutStageCallback数据 OUT 阶段回调
HAL_PCD_DisconnectCallback中断连接回调
HAL_PCD_ISOINIncompleteCallbackISO IN 事务回调
HAL_PCD_ISOOUTIncompleteCallbackISO OUT 事务回调
HAL_PCD_ResetCallbackUSB 复位回调
HAL_PCD_ResumeCallbackUSB 重新开始回调
HAL_PCD_SetupStageCallback设置阶段回调
HAL_PCD_SOFCallback帧回调起始
HAL_PCD_SuspendCallback挂起回调

6.1.5 配置 USB 设备固件库

  可使用 usbd_conf.h 文件配置 USB 设备库。
  usbd_conf.h 为特定的配置文件,用于定义一些全局参数及特定配置。usbd_conf.c 文件为接口文件,用于将上层库与 HAL 驱动和 BSP 驱动链接起来。
在这里插入图片描述
在这里插入图片描述
  注 : 用户可从 STM32Cube 提供的usbd_conf.h 文件开始。此文件也可复制到应用目录,根据应用需求修改。
  注 : 默认情况下,对于 USB 设备样例,库与用户消息并不会显示在 LCD上。
  但是用户可实现自己的消息 (若要将库消息重定向到 LCD 屏幕上,需将lcd_log.c 驱动添加到应用源中)并可选择是否显示,这可通过符合应用要求的方式修改配置文件“usbd_conf.h” 中的 USBD_DEBUG_LEVEL 实现,该文件位于项目的包含目录下,修改方式为:
  0:无日志 / 调试消息
  1:使能日志消息
  2:使能日志和调试消息

6.1.6 USB 控制功能

  用户应用可受益于 USB 设备中包含的一些 USB 功能,例如:
  设备复位 当设备从 USB 收到复位信号时,库会复位,并初始化软硬件上的应用。此函数为中断程序的一部分。
  设备挂起 当设备检测到 USB 上的挂起条件,库会停止所有操作,并将系统置于挂起状态(前提条件为 usbd_conf.c 文件中使能了低功耗模式管理)。
  设备重新开始 当设备检测到 USB 上的重新开始信号时,库会恢复 USB 内核时钟并将系统置于空闲状态 (前提条件为 usbd_conf.c 文件中使能了低功耗模式管理)。

6.2 USB 设备库功能

6.2.1 USB 设备 - 内核文件

  Core 目录包含了 USB 设备库状态机,它由通用串行总线规范版本 2.0 定义。

文件说明
usbd_core (.c, .h)此文件包含了处理所有 USB 通信和状态机的函数。
usbd_req(.c,.h)此文件包含了 USB 规范的第 9 章列出的请求实现。
usbd_ctlreq(.c,.h)此文件处理 USB 事务结果。
usbd_conf_template(.c,.h)为底层接口文件的模板文件,用户应对其修改并包括在应用文件中
usbd_def(.c, .h)通用的库定义

6.2.2 USB 设备 - 类驱动文件

  Class 目录包含与类实现有关的所有文件,满足了这些类中协议构建规范的要求。
在这里插入图片描述

6.2.3 usbd_core (.c,.h) 文件功能

在这里插入图片描述
在这里插入图片描述

6.2.4 usbd_ioreq (.c,.h) 文件功能

在这里插入图片描述
在这里插入图片描述

6.2.5 usbd_ctrlq (.c,.h) 文件功能

在这里插入图片描述

6.3 USB 设备类接口

  在USB设备库初始化期间选择USB类,方法是选择响应的类回调结构体。类结构体如下定义:

6.3.1 USB 类回调结构体 (usbd_def.h)

typedef struct _Device_cb
{
  uint8_t (*Init)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx);
  uint8_t (*DeInit)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx);
  /* Control Endpoints*/
  uint8_t (*Setup)(struct _USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef  *req);
  uint8_t (*EP0_TxSent)(struct _USBD_HandleTypeDef *pdev);
  uint8_t (*EP0_RxReady)(struct _USBD_HandleTypeDef *pdev);
  /* Class Specific Endpoints*/
  uint8_t (*DataIn)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);
  uint8_t (*DataOut)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);
  uint8_t (*SOF)(struct _USBD_HandleTypeDef *pdev);
  uint8_t (*IsoINIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);
  uint8_t (*IsoOUTIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);

  uint8_t  *(*GetHSConfigDescriptor)(uint16_t *length);
  uint8_t  *(*GetFSConfigDescriptor)(uint16_t *length);
  uint8_t  *(*GetOtherSpeedConfigDescriptor)(uint16_t *length);
  uint8_t  *(*GetDeviceQualifierDescriptor)(uint16_t *length);
#if (USBD_SUPPORT_USER_STRING_DESC == 1U)
  uint8_t  *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev, uint8_t index,  uint16_t *length);
#endif

} USBD_ClassTypeDef;

Init:当设备收到设置配置请求时,会调用此回调;在此函数中类接口使用的端点打开。
DeInit:当收到清除配置请求时,会调用此回调;此函数会关闭类接口使用的端点。

/* Control Endpoints*/
Setup:调用此回调可处理特定类设置请求。
EP0_TxSent:当发送状态完成时,会调用此回调。
EP0_RxSent:当接收状态完成时,会调用此回调。

/* Class Specific Endpoints*/
DataIn:调用此回调可执行非控制端点相关数据输入阶段的数据。
DataOut:调用此回调可执行非控制端点相关数据输出阶段的数据。
SOF:当收到 SOF 中断时调用此回调;可使用此回调将一些过程与帧开始同步。
IsoINIncomplete:当最后一个同步 IN 传输未完成时,调用此回调。
IsoOUTIncomplete:当最后一个同步 OUT 传输未完成时,调用此回调。

GetHSConfigDescriptor:此回调返回 HS USB 配置描述符。
GetFSConfigDescriptor:此回调返回 FS USB 配置描述符。
GetOtherSpeedConfigDescriptor:此回调返回高速模式中所用类的其它配置描述符。
GetDeviceQualifierDescriptor:此回调返回设备合格描述符

  库还提供了描述符回调结构体,以允许用户在应用运行时管理设备和字符串描述符。描述符结构体如下定义:

6.3.1.1 USB 设备描述符结构体 (usbd_def.h)
/* USB Device descriptors structure */
typedef struct
{
  uint8_t  *(*GetDeviceDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  uint8_t  *(*GetLangIDStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  uint8_t  *(*GetManufacturerStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  uint8_t  *(*GetProductStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  uint8_t  *(*GetSerialStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  uint8_t  *(*GetConfigurationStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  uint8_t  *(*GetInterfaceStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
#if (USBD_LPM_ENABLED == 1U)
  uint8_t  *(*GetBOSDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
#endif
} USBD_DescriptorsTypeDef;

GetDeviceDescriptor:此回调返回设备描述符。
GetLangIDStrDescriptor:此回调返回语言 ID 字符串描述符。
GetManufacturerStrDescriptor:此回调返回制造商字符串描述符。
GetProductStrDescriptor:此回调返回产品字符串描述符。
GetSerialStrDescriptor:此回调返回序列号字符串描述符。
GetConfigurationStrDescriptor:此回调返回配置字符串描述符。
GetInterfaceStrDescriptor:此回调返回接口字符串描述符。

  注 : USB 设备样例内提供的 usbd_desc.c 文件实现了这些回调实体。

七、USB 设备库类模块

  类模块包含了关于类实现的所有文件。它与这些类中构建协议的规范兼容。下表展示了MSC、 HID、 DFU、音频、 CDC 类的 USB 设备类文件。

7.1 USB 设备类文件表

在这里插入图片描述

7.1.1 HID 类

HID 类实现

此驱动实现了规范的下列方面:

  • 启动接口子类
  • 鼠标协议
  • 使用页:通用桌面
  • 使用:摇杆
  • 收集:应用
HID 用户接口

  输入报告仅通过中断进入管道发送 (HID 鼠标样例)。
  必须通过控制管道或中断输出管道,由主机启动特性和输出报告 (自定义HID 样例)
  USBD_HID_SendReport可被HID鼠标应用使用,来发送HID报告,在这个版本中,HID驱动仅处理 IN 传输。此函数的用法举例如下所示:

 static __IO uint32_t counter=0;
 HAL_IncTick();
 /* check Joystick state every 10ms */
 if (counter++ == 10)
 { 
 GetPointerData(HID_Buffer);
 /* send data though IN endpoint*/
 if((HID_Buffer[1] != 0) || (HID_Buffer[2] != 0))
 {
 USBD_HID_SendReport(&USBD_Device, HID_Buffer, 4);
 }
 counter =0;
 }
 Toggle_Leds();
}
HID 类驱动 API

  所有 HID 类驱动 API 都定义于 usbd_hid.c 中,并总结于下表中

函数说明
static uint8_t USBD_HID_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)初始化 HID 接口,打开所用的端点。
static uint8_t USBD_HID_DeInit(USBD_HandleTypeDef *pdev,uint8_t cfgidx)解除初始化 HID 层,关闭所用的端点。
static uint8_t USBD_HID_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)处理 HID 特定请求。
uint8_t USBD_HID_SendReport(USBD_HandleTypeDef *pdev,uint8_t *report, uint16_t len)发送 HID 报告。

  HID栈由调用USBD_HID_Init()来初始化,之后应用必须调用USBD_HID_SendReport()函数以发送 HID 报告。

  下列 HID 特定请求通过端点 0 (控制)实现:

#define HID_REQ_SET_PROTOCOL 0x0B
#define HID_REQ_GET_PROTOCOL 0x03
#define HID_REQ_SET_IDLE 0x0A
#define HID_REQ_GET_IDLE 0x02
#define HID_REQ_SET_REPORT 0x09
#define HID_REQ_GET_REPORT 0x01

  IN 端点地址和可发送的最大字节数由这些定义给出:

#define HID_EPIN_ADDR 0x81
#define HID_EPIN_SIZE 0x04

7.1.2 大容量存储类 (略)

7.1.3 设备固件升级(DFU)类 (略)

7.1.4 音频类

  此驱动管理音频类,符合 “ 音频设备 USB 设备类定义 V1.0 1998-3-18”。
此驱动实现了规范的下列方面:

  • 设备描述符管理
  • 配置描述符管理
  • 标准 AC 接口描述符管理
  • 1 个音频流接口 (单通道、 PCM、立体声模式)
  • 1 个音频流端点
  • 1 个音频终端输入 (1 个通道)
  • 音频特定类 AC 接口
  • 音频特定类 AS 接口
  • 音频控制请求:仅支持 SET_CUR 和 GET_CUR 请求 (静音)
  • 音频特性单元 (限为静音控制)
  • 音频同步类型:异步
  • 单固定音频采样率 (可在 usbd_conf.h 文件中配置)

  注 : 音频类基于 USB 规范 1.0,因此仅支持低速模式和全速模式,不支持高速传输。请参考 “ 音频设备 USB 设备类定义 V1.0 1998-3-18” 以获取更详细信息。

  可针对特定用户应用增加或修改这些方面。
  此驱动没有实现规范的下述方面 (但有可能修改驱动以管理这些特性):

  • 音频控制端点管理
  • SET_CUR 和 GET_CUR 以外的音频控制请求
  • 音频控制请求的抽象层 (仅管理静音功能)
  • 音频同步类型:自适应
  • 音频压缩模块和接口
  • MIDI 接口和模块
  • 混合 / 选择 / 处理 / 扩展单元 (所列单元限为静音控制)
  • 其它任何特定应用模块
  • 复合及可变的音频采样率
  • 音频输出流端点 / 接口 (麦克风)
音频类实现

  音频传输基于同步端点事务。 音频控制请求还通过控制端点 (端点 0)管理。
  在每一帧传输的音频数据包必须在此帧时间之内 (下一帧之前)处理掉。音频质量取决于数据传输和数据处理之间的同步情况。此驱动依赖所交付 I2S时钟的精度,实现简单的同步机制。在每帧开始时,驱动会检查是否正确执行了前一帧的处理,若仍在进行则将其停止。 为防止任何数据覆盖,主要使用了两种保护方式:

  1. 在 USB 缓冲和输出设备寄存器 (I2S)之间使用 DMA 进行数据传输。
  2. 使用多缓冲存储从 USB 接收的数据。

  基于此机制,如果时钟精度或处理速率不够高,则会导致较差的音频质量。
  此机制可通过实现更灵活的音频流控制来加强,如 USB 反馈模式、动态音频时钟纠正,或使用 SOF 事件生成 / 控制音频时钟。
  驱动还支持基本音频控制请求。为简化驱动,仅实现了两个请求。然而,仅需稍微修改音频内核驱动即可支持其他请求。
在这里插入图片描述

音频内核文件 usbd_audio (.c, .h)

  usbd_audio (.c, .h)此驱动为音频内核。它管理音频数据传输并控制请求。它不直接处理音频硬件 (由底层驱动管理)。

函数说明
static uint8_t USBD_AUDIO_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);初始化音频接口。
static uint8_t USBD_AUDIO_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);解除初始化音频接口。
static uint8_t USBD_AUDIO_Setup(USBD_HandleTypeDef *pdev,USBD_SetupReqTypedef *req);处理音频控制请求解析。
static uint8_t *USBD_AUDIO_GetCfgDesc(uint16_t *length);获取配置描述符
static uint8_t *USBD_AUDIO_GetDeviceQualifierDesc(uint16_t *length);获取设备限定符描述符
static uint8_t USBD_AUDIO_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);处理音频输入数据阶段。
static uint8_t USBD_AUDIO_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum);处理音频输出数据阶段。
static uint8_t USBD_AUDIO_EP0_RxReady(USBD_HandleTypeDef *pdev);处理 EP0 Rx 事件
static uint8_t USBD_AUDIO_EP0_TxReady(USBD_HandleTypeDef *pdev);处理 EP0 Tx 事件
static uint8_t USBD_AUDIO_SOF(USBD_HandleTypeDef *pdev);处理 SOF 事件 (数据缓冲更新和同步)。
static uint8_t USBD_AUDIO_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum);处理 IsoINIncomplete 数据输入情况。
static uint8_t USBD_AUDIO_IsoOutIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum);处理 IsoINIncomplete 数据输出情况。
static void AUDIO_REQ_GetCurrent(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);处理 GET_CUR 音频控制请求。
static void AUDIO_REQ_SetCurrent(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);处理 SET_CUR 音频控制请求。

  底层硬件接口通过它们相应的驱动结构体管理:

typedef struct
{
  int8_t (*Init)(uint32_t  AudioFreq, uint32_t Volume, uint32_t options);
  int8_t (*DeInit)(uint32_t options);
  int8_t (*AudioCmd)(uint8_t *pbuf, uint32_t size, uint8_t cmd);
  int8_t (*VolumeCtl)(uint8_t vol);
  int8_t (*MuteCtl)(uint8_t cmd);
  int8_t (*PeriodicTC)(uint8_t cmd);
  int8_t (*GetState)(void);
} USBD_AUDIO_ItfTypeDef;

  每个音频硬件接口驱动都应该提供一个类型为USBD_AUDIO_ItfTypeDef的结构体指针。(下边的章节会写该结构体所指向的函数和变量)。如果给定的存储器接口不支持某功能,则相应的字段置为 NULL 值。

usbd_audio_if (.c, .h)

  usbd_audio_if (.c, .h)此驱动管理底层音频硬件。 usbd_audio_if.c/.h 驱动管理音频输出接口 (从 USB 到音频扬声器 / 耳机)。用户可调用底层编解码器驱动(即 stm324xg_eval_audio.c/.h)以进行基本的音频操作 (播放 / 暂停 / 音量控制…)。

此驱动提供了结构体指针:

/** AUDIO_IF Interface callback. */
extern USBD_AUDIO_ItfTypeDef USBD_AUDIO_fops_FS;

usbd_audio_if (.c,.h)文件中函数说明。

函数说明
static int8_t AUDIO_Init_FS(uint32_t AudioFreq, uint32_t Volume, uint32_t options);初始化音频接口。
static int8_t AUDIO_DeInit_FS(uint32_t options);解除初始化音频接口,释放所用的资源。
static int8_t AUDIO_AudioCmd_FS(uint8_t* pbuf, uint32_t size, uint8_t cmd);处理音频播放器指令 (播放、暂停 …)
static int8_t AUDIO_VolumeCtl_FS(uint8_t vol);处理音频播放器音量控制。
static int8_t AUDIO_MuteCtl_FS(uint8_t cmd);处理音频播放器静音状态。
static int8_t AUDIO_PeriodicTC_FS(uint8_t cmd);处理当前数据包传输的结束(当前版本的驱动程序不需要)
static int8_t AUDIO_GetState_FS(void);返回驱动音频播放器的当前状态 (正在播放 / 已暂停 / 错误 …)。

通过下列状态列表来获得当前音频播放器的状态:
在这里插入图片描述

怎样使用此驱动:

  此驱动使用了硬件驱动的抽象层(即 HW 编解码器、I2S 接口、I2C 控制接口 …)。此抽象通过底层 (即 usbd_audio_if.c)执行,您可根据您的应用以及相应的硬件对其修改。
若要使用此驱动:
通过文件 usbd_conf.h,您可配置:

  • 音频采样率 (定义 USBD_AUDIO_FREQ)

  在启动时调用函数 USBD_AUDIO_Init(),配置所有必要的固件和硬件部件(特定应用的硬件配置函数也由此函数调用)硬件部件由底层接口 (即usbd_audio_if.c)管理,可由用户根据 应用需要修改。
整个传输由下述函数管理 (用户不需为输出传输调用任何函数):

  • usbd_audio_DataIn()和usbd_audio_DataOut()使用收到的或发送的数据更新音频缓冲。对于输出传输,当收到数据时,它们会被直接复制到音频缓冲中,写缓冲 (wr_ptr)增加。

  音频控制请求由函数 USBD_AUDIO_Setup() 和USBD_AUDIO_EP0_RxReady() 管理。这些函数会将音频控制请求路由至底层 (即 usbd_audio_if.c) 。在当前版本中,仅管理了SET_CUR 和 GET_CUR 请求,仅用于静音控制。

音频的已知限制

  如果配置了低音频采样率(将USBD_AUDIO_FREQ定义为24 kHz以下),则在暂停/重新开始 / 停止操作时可能导致噪声问题。这是由于在停止 I2S 时钟和发送静音指令到外部编解码器之间的软件时序调节。
  支持的音频采样率为:96 kHz到24 kHz(此驱动不支持非整数kHz值,例如11.025 kHz、22.05 kHz 或 44.1 kHz)。对于 1000 Hz 的整数倍数频率,主机会在每帧 (1 ms)发送 整数个字节。当频率不是 1000Hz 的整数倍时,主机会在每帧发送非整数个字节。实际上,这是通过发送不同大小的帧来管理的 (即对于 22.05 kHz,主机将发送 19 帧的 22 字节和一帧的 23 字节)。音频内核不会管理此大小差别,多余的字节会一直被忽略。建议设置高采样率和标准采样率,以得到最好的音频质量 (即 96 kHz 或 48 kHz)。请注意,最大允许的音频频率为 96 kHz(此限制的原因是评估板上使用的编解码器。STM32 I2S 单元可达到 192 kHz)。

7.1.5 通信设备类 (CDC) (略)

7.1.6 添加自定义类 (略)

7.1.7 库大小优化

  在本节中,我们回顾一些基本技巧,涉及怎样优化 USB 设备库之上开发的应用大小。
  缩小 USB 样例是一个重要的目标,尤其对于具有较少 Flash/RAM 内存的 STM32 产品 (如STM32 L0 和 F0)来说尤其重要。

降低堆和栈大小设置 (在连接 (linker)文件中)

栈为程序存储的内存区,例如:

  • 本地变量
  • 返回地址
  • 函数参数
  • 编译器临时量
  • 中断上下文

  如果您的连接器配置保留了大量的堆和栈,而您的应用并不需要,您可以决定其合适的大小。

尽可能使用局部变量,而不用全局变量

  如果一个变量仅在一个函数中使用,那么应在函数内将其声明为局部变量。

常量应在闪存中分配

  建议将永不变化的所有常量全局变量分配至只读区。例如,使用 C 关键字 “const” 将 USB 描述符声明为常量。
例如:
在这里插入图片描述

使用静态内存分配,而不是 malloc

  USB 设备库为类处理结构体使用动态内存分配以支持多实例 (在双核工作情况下),这意味着我们可以将同一 USB 类用于 USB 的两个实例 (HS 和FS)。
  使用动态分配的第二个原因是当 USB 不再使用时,可释放内存。然而,动态内存分配会增加一些空间上的开销,主要是 ROM 内存。因此,对于低内存 STM32设备,当不需要多实例支持时,建议使用静态分配。在这种情况下,需要声明静态缓冲,大小为类处理结构体的大小。
下面是一个实现样例:

  1. 在 usbd_conf.h 文件中,定义内存静态分配和程序;
USBD_static_malloc()USBD_static_free()
#define MAX_STATIC_ALLOC_SIZE 4 /* HID */ 类类类类类类
#define USBD_malloc (uint32_t *)USBD_static_malloc
#define USBD_free USBD_static_free
  1. 在 usbd_conf.c 文件中如下实现:
/**
  * @brief  Static single allocation.
  * @param  size: Size of allocated memory
  * @retval None
  */
void *USBD_static_malloc(uint32_t size)
{
  /* static uint8_t mem[sizeof(USBD_AUDIO_HandleTypeDef)]; */
  /* USER CODE BEGIN 4 */
  /**
  * To compute the request size you must use the formula:
    AUDIO_OUT_PACKET = (USBD_AUDIO_FREQ * 2 * 2) /1000)
    AUDIO_TOTAL_BUF_SIZE = AUDIO_OUT_PACKET * AUDIO_OUT_PACKET_NUM with
	Number of sub-packets in the audio transfer buffer. You can modify this value but always make sure
    that it is an even number and higher than 3
	AUDIO_OUT_PACKET_NUM = 80
  */
  static uint8_t mem[512];
  /* USER CODE END 4 */
  return mem;
}

/**
  * @brief  Dummy memory free
  * @param  p: Pointer to allocated  memory address
  * @retval None
  */
void USBD_static_free(void *p)
{

}

八、常见问题

1. 怎样在运行时修改设备和字符串描述符 ?

  在 usbd_desc.c 文件中,可使用 Get Descriptor 回调修改设备和字符串相关的描述符。 应用可使用 switch case 语句,返回应用索引相关的正确描述符缓冲。

2. 大容量存储类怎样支持超过一个逻辑单元 (LUN) ? (略)

3. 端点地址在哪里定义 ?

  端点地址在类驱动的头文件中定义。例如,对于 MSC 演示,IN/OUT 端点地址如下定义在 usbd_msc.h 文件中:

#define MSC_EPIN_ADDR 0x81 For Endpoint 1 IN
#define MSC_EPOUT_ADDR 0x01 For Endpoint 1 OUT

4. USB 设备库可配置为在高速或全速模式运行吗?

  是的,库可处理 USB OTG HS 和 USB OTG FS 内核,如果 USB OTG FS 内核仅能工 作于全速模式, USB OTG HS 可工作于高速或全速模式。若要选择合适的 USB 内核,用户必须在编译预处理器内增加下列宏定义 (在样例提供的预配置项目中已经完成):

- "USE_USB_HS" 当使用 USB 高速 (HS)内核时
- "USE_USB_FS" 当使用 USB 全速 (FS)内核时
- "USE_USB_HS""USE_USB_HS_IN_FS" 当在 FS 模式使用 USB 高速(HS)内核时

5. 怎样在 USB 设备类驱动中更改所用的端点?

若要更改端点或增加一个新端点,请:
a) 使用 USBD_LL_OpenEP() 执行端点初始化。
b) 对于在 USBD_LL_Init() 函数中使用这些 API 的 usb_conf.c 文件,配置新定义端点的 TX 或 Rx FIFO 大小。

  • 对于 STM32F2 和 STM32F4 系列 (FS 和 HS 内核):

HAL_PCD_SetRxFiFo();
HAL_PCD_SetTxFiFo();

  Rx 和 Tx FIFO 的总大小应该小于所用内核的总 FIFO 大小(对于 USB OTG FS 内核,为 320 x 32 比特;对于 USB OTG HS 内核,为 1024 x 32 比特)。

  • 对于 STM32F0、 STM32L0、 STM32F1 和 STM32F3 系列 (仅 FS 内核):

HAL_PCD_PMA_Config();

6. USB 设备库与实时操作系统 (RTOS)兼容吗 ?

  是的,USB 设备库可与 RTOS 共用使用,CMSIS RTOS 封装的作用是对 OS 内核抽象。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值