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 内核抽象。

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: STM32 USB Device Library 是专为 STM32 微控制器设计的 USB 设备。它提供了一套 API 接口供开发者使用,以便在 STM32 上实现 USB 设备功能。 要下载 STM32 USB Device Library,首先我们需要访问 ST 微电子官方网站。在该网站上,我们可以找到 STM32Cube 软件开发套件的下载页面。在这个页面上,我们可以选择需要的 STM32 系列以及芯片型号,并下载相应的软件包。 在 STM32Cube 软件开发套件中,STM32 USB Device Library 是其中的一部分。下载和安装 STM32Cube 软件开发套件后,我们就可以获得 STM32 USB Device Library 的最新版本。 一旦获得了 STM32 USB Device Library,我们可以将其添加到我们的项目中。我们可以通过复制文件或者链接的方式将 STM32 USB Device Library 添加到我们的 IDE(集成开发环境)中。接下来,我们需要在我们的代码中包含 USB Device Library 的头文件,并使用提供的 API 接口来实现 USB 设备功能。 当我们完成编写代码和配置项目后,我们可以编译和下载我们的代码到 STM32 微控制器上。通过正确配置和使用 STM32 USB Device Library,我们的 STM32 设备可以通过 USB 接口与计算机或其他 USB 主机设备进行通信。 总之,STM32 USB Device Library 是用于实现 USB 设备功能的,可以通过访问 ST 微电子官方网站并下载 STM32Cube 软件开发套件来获取最新版本。下载并添加文件后,我们可以在代码中使用提供的 API 接口来实现我们的 USB 设备功能。 ### 回答2: STM32 USB device library是STMicroelectronics官方提供的用于STM32微控制器的USB设备。该包含了用于实现USB设备功能的各种驱动程序和例程。 要下载STM32 USB device library,您可以按照以下步骤进行操作: 1. 访问STMicroelectronics官方网站。您可以通过搜索引擎输入关键词"STMicroelectronics"来找到官方网站。 2. 在官方网站首页或导航栏中,找到"产品"或"开发工具"类别,并点击进入。 3. 在"产品"或"开发工具"类别中,选择"微控制器"或"STM32系列",会显示出支持的STM32微控制器的产品列表或开发工具。 4. 在产品列表中,找到您所使用的STM32微控制器型号,并点击进入相关页面。 5. 在该页面中,应该能找到相关的软件、固件和驱动程序。在其中寻找与USB设备相关的下载链接。 6. 点击下载链接,选择适合您的操作系统的版本,并进行下载。 另外,您也可以通过在搜索引擎中输入"STM32 USB device library下载"等关键词来查找其他第三方网站或开发者社区,这些网站可能提供了更多的资源和下载选项。 一旦您下载了STM32 USB device library,您可以解压缩并将其添加到您的STM32项目中。然后,您可以按照官方提供的文档和示例代码来使用该,实现您所需的USB设备功能。 请注意,为了使用STM32 USB device library,您需要具备一定的嵌入式系统和STM32微控制器的编程知识。建议您在使用该之前仔细阅读相关的官方文档和示例代码,以确保正确地集成和使用该。 ### 回答3: STM32 USB Device是STMicroelectronics专为其STM32系列微控制器开发的一款USB设备协议。对于需要在STM32微控制器上实现USB设备功能的开发者来说,这个是非常有用的。 要下载STM32 USB Device,可以按照以下步骤操作: 1. 首先,访问STMicroelectronics的官方网站。在搜索栏中输入“STM32 USB Device”进行搜索。 2. 在搜索结果中找到STMicroelectronics官方网站上的下载页面或者产品页面。点击进入该页面。 3. 在该页面寻找能够下载STM32 USB Device的链接或者按钮。通常,这些链接或按钮位于页面的底部或者页面上的特定区域。 4. 点击下载链接或按钮,开始下载STM32 USB Device的压缩文件。这个文件包含了的源代码和相应的文档。 5. 下载完成后,解压缩文件到一个你指定的文件夹中。 一旦STM32 USB Device被下载和解压缩,你可以在你的STM32工程项目中使用该。使用该的具体步骤可能因为不同的开发环境或者IDE而有所不同。一般来说,在你的开发环境中,你需要将的源代码加入到你的项目中,并且配置项目设置以允许使用该。 使用STM32 USB Device,你可以方便地在STM32微控制器上实现各种USB设备功能,比如USB存储设备、虚拟串口设备等。这个提供了丰富的API和示例代码,帮助你快速构建并调试你的USB设备应用程序。 总之,STM32 USB Device是STMicroelectronics为STM32微控制器开发的一款USB设备协议,可以通过STMicroelectronics的官方网站进行下载,并提供了丰富的API和示例代码帮助你在STM32微控制器上实现USB设备功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值