本章摘取《STM32 USB-FS-Device development kit》文档。
一、概述
STM32 USB-FS-Device开发套件是一个完整的固件和软件包,包括所有USB传输类型(控制、中断、批量和同步)的示例和例程。
本文档介绍了STM32 USB-FS-Device开发套件的所有组件,包括:
- STM32 USB-FS-Device 库:与默认端点和标准请求相关的所有进程。
- 设备固件升级 (DFU) 例程:控制传输
- 操纵杆鼠标例程:中断传输
- 自定义 HID 例程:中断传输
- 大容量存储例程:批量传输
- 虚拟 COM 端口例程:中断和批量传输
- CDC 环回例程:中断和批量传输
- 复合例程:中断和批量传输
- USB 语音扬声器例程(USB 扬声器):同步传输
二、STM32 USB-FS-Device 固件库
本节介绍用于管理STM32 USB 2.0全速设备外设的固件接口(称为USB-FS-Device库)。在本文档的其余部分中,它将被称为 USB-FS_Device 外围设备 。
该固件库的主要目的是提供资源,以简化使用STM32微控制器系列中USB-FS_Device外设为每种USB传输类型的应用开发。
1、USB 应用程序层次结构
图 1 显示了典型 USB 应用的不同组件与 USB-FS-Device 库之间的交互。
如图 1 所示,USB-FS-Device 库分为两层:
- STM32_USB-FS_Device_Driver:该层管理与USB-FS_Device外设和USB标准协议的直接通信。STM32_USBFS_Device_Driver符合USB 2.0规范,独立于标准STM32标准外设库。
- Application Interface layer:该层为用户提供了库核心和最终应用程序之间的完整接口。
注:USB-FS外设接口层被加载(通过在编译时定义)并用作外围接口层。
应用程序接口层和最终的应用程序可以与标准外设库通信,以管理应用程序的硬件需求。
这些层的详细说明以及编码规则将在下一节中提供。
图 2 显示了 USB-FS-Device 库的软件包组织结构,其中包含所有演示和子文件夹。
2、USB-FS_Device外设接口
表 4 显示了 USB-FS_Device 外设接口模块。
1、usb_reg(.h, .c)
usb_regs 模块实现了硬件抽象层,它提供了一组用于访问 USB-FS_Device 外设寄存器的基本功能。
注:可用的函数有两个调用版本:
- 作为宏:调用是:_NameofFunction(parameter1, …)
- 作为子例程:调用是:NameofFunction(parameter1, …)
1、常用寄存器函数
表5中的函数可用于设置或获取各种常见的USB-FS_Device外设寄存器。
2、端点寄存器函数
端点寄存器的所有操作都可以通过 SetENDPOINT 和 GetENDPOINT 函数获得。但是,许多函数都是从这些函数派生出来的,以提供对特定字段直接操作的优势。
a、端点set/get价值
// bEpNum:端点号
// wRegValue:要写入的值
void SetENDPOINT(uint8_t bEpNum,uint16_t wRegValue);
// bEpNum:端点号
// 返回值:端点寄存器值
uint16_t GetENDPOINT(uint8_t bEpNum)
b、端点 TYPE 字段
端点寄存器的EP_TYPE字段可以采用下面定义的值:
#define EP_BULK (0x0000) // 端点批量
#define EP_CONTROL (0x0200) // 端点控制
#define EP_ISOCHRNOUS (0x0400) // 端点同步
#define EP_INTERRUPT (0x0600) // 端点中断
// bEpNum:端点号
// wtype:端点类型
void SetEPType (uint8_t bEpNum, uint16_t wtype);
// bEpNum:端点号
// 返回值:端点类型
uint16_t GetEPType (uint8_t bEpNum);
c、端点 STATUS 字段
端点寄存器的STAT_TX/STAT_RX字段可以假定以下定义的值:
#define EP_TX_DIS (0x0000) // Endpoint TX DISabled
#define EP_TX_STALL (0x0010) // Endpoint TX STALLed
#define EP_TX_NAK (0x0020) // Endpoint TX NAKed
#define EP_TX_VALID (0x0030) // Endpoint TX VALID
#define EP_RX_DIS (0x0000) // Endpoint RX DISabled
#define EP_RX_STALL (0x1000) // Endpoint RX STALLed
#define EP_RX_NAK (0x2000) // Endpoint RX NAKed
#define EP_RX_VALID (0x3000) // Endpoint RX VALID
// bEpNum:端点号
// wState:端点状态
void SetEPTxStatus(uint8_t bEpNum,uint16_t wState);
void SetEPRxStatus(uint8_t bEpNum,uint16_t wState);
// bEpNum:端点号
// 返回值:端点状态
uint16_t GetEPTxStatus(uint8_t bEpNum);
uint16_t GetEPRxStatus(uint8_t bEpNum);
d、端点 KIND 字段
// bEpNum:端点号
void SetEP_KIND(uint8_t bEpNum);
void ClearEP_KIND(uint8_t bEpNum);
// bEpNum:端点号
void Set_Status_Out(uint8_t bEpNum);
void Clear_Status_Out(uint8_t bEpNum);
// bEpNum:端点号
void SetEPDoubleBuff(uint8_t bEpNum);
void ClearEPDoubleBuff(uint8_t bEpNum);
// 正确的传输Rx/Tx字段
// bEpNum:端点号
void ClearEP_CTR_RX(uint8_t bEpNum);
void ClearEP_CTR_TX(uint8_t bEpNum);
e、数据切换Rx/Tx字段
// bEpNum:端点号
void ToggleDTOG_RX(uint8_t bEpNum);
void ToggleDTOG_TX(uint8_t bEpNum);
d、地址字段
// bEpNum:端点号
// bAddr:要设置的地址
void SetEPAddress(uint8_t bEpNum,uint8_t bAddr);
// bEpNum:端点号
uint8_t GetEPAddress(uint8_t bEpNum);
3、缓冲区描述表函数
这些函数用于设置或获取端点接收和发送缓冲区的地址和大小。
a、Tx/Rx 缓冲区地址字段
// bEpNum:端点号
// wAddr:要设置的地址
void SetEPTxAddr(uint8_t bEpNum,uint16_t wAddr);
void SetEPRxAddr(uint8_t bEpNum,uint16_t wAddr);
// bEpNum:端点号
// 返回值:要设置的地址
uint16_t GetEPTxAddr(uint8_t bEpNum);
uint16_t GetEPRxAddr(uint8_t bEpNum);
b、Tx/Rx 缓冲区计数器字段
// bEpNum:端点号
// wCount:要设置的计数值
void SetEPTxCount(uint8_t bEpNum,uint16_t wCount);
void SetEPRxCount(uint8_t bEpNum,uint16_t wCount);
// bEpNum:端点号
// 返回值:要设置的计数值
uint16_t GetEPTxCount(uint8_t bEpNum);
uint16_t GetEPRxCount(uint8_t bEpNum);
4、双缓冲端点函数
为了在批量或同步模式下获得高数据传输吞吐量,必须对双缓冲模式进行编程。在此操作模式下,端点寄存器和缓冲区描述表单元的某些字段具有不同的含义。
为了便于使用此功能,已经开发了几个功能。
1、SetEPDoubleBuff
编程为在批量模式下工作的端点可以通过设置 EP-KIND 位来设置为双缓冲。函数 SetEPDoubleBuff() 完成此任务:
// bEpNum:端点号
void SetEPDoubleBuff(uint8_t bEpNum);
2、FreeUserBuffer
在双缓冲模式下,端点变为单向,并应用未使用方向的缓冲区描述表单元格来处理第二个缓冲区。
地址和计数器必须以不同的方式处理。Rx 和 Tx 地址以及计数器单元格将成为 Buffer0 和 Buffer1 单元格。库中提供了专用于此操作模式的功能。
在批量传输期间,行填充一个缓冲区,而另一个缓冲区保留给应用程序。用户应用程序必须在需要缓冲区的批量到达之前处理数据。必须及时释放保留给应用程序的缓冲区。
为了从应用程序中释放使用中的缓冲区,提供了FreeUserBuffer函数:
// bEpNum:端点号
// bDir:端点方向
void FreeUserBuffer(uint8_t bEpNum, uint8_t bDir);
a、双缓冲区地址
对于双缓冲模式,这些函数在缓冲区描述表中设置或获取缓冲区地址值。
// bEpNum:端点号
// wBuf0Addr、wBuf1Addr:缓冲区地址
void SetEPDblBuffAddr(uint8_t bEpNum,uint16_t wBuf0Addr,uint16_t wBuf1Addr);
void SetEPDblBuf0Addr(uint8_t bEpNum,uint16_t wBuf0Addr);
void SetEPDblBuf1Addr(uint8_t bEpNum,uint16_t wBuf1Addr);
// bEpNum:端点号
// 返回值:缓冲区地址
uint16_t GetEPDblBuf0Addr(uint8_t bEpNum);
uint16_t GetEPDblBuf1Addr(uint8_t bEpNum);
b、双缓冲计数器
这些函数在缓冲区描述表中为双缓冲模式设置或获取缓冲区计数器值。
// bEpNum:端点号
// bDir:端点方向
// wCount:缓冲计数器
void SetEPDblBuffCount(uint8_t bEpNum, uint8_t bDir, uint16_t wCount);
void SetEPDblBuf0Count(uint8_t bEpNum, uint8_t bDir, uint16_t wCount);
void SetEPDblBuf1Count(uint8_t bEpNum, uint8_t bDir, uint16_t wCount);
// bEpNum:端点号
// 返回值:缓冲计数器
uint16_t GetEPDblBuf0Count(uint8_t bEpNum);
uint16_t GetEPDblBuf1Count(uint8_t bEpNum);
c、双缓存区状态
除了双缓冲区模式的STALL状态外,简单缓冲区模式和双缓冲区模式使用相同的函数来管理端点 STATUS。
此功能由函数管理:
// bEpNum:端点号
// bDir:端点方向
void SetDouBleBuffEPStall(uint8_t bEpNum,uint8_t bDir);
2、usb_int (.h , .c)
usb_int模块处理正确的传输中断服务例程;它提供 USB 设备协议事件和库之间的链接。
STM32 USB-FS设备外设提供两个传输例程:
- 低优先级中断:由函数CTR LP()管理,用于控制、中断和批量(在简单的缓冲区模式)。
- 高优先级中断:由函数CTR HP()管理,并用于更快的传输模式,如同步和批量(在双缓冲模式)。
3、usb_mem (.h , .c)
usb_mem将缓冲区数据从用户内存区域复制到数据包内存区域 (PMA),反之亦然。它提供两种不同的功能:
// pbUsrBuf 是指向产品 SRAM 中用户内存区域的指针。
// wPMABufAddr 是 PMA(专用于 USB 的 512 字节数据包存储区域)中的地址。
// wNBytes 是要复制的字节数。
void PMAToUserBufferCopy(uint8_t *pbUsrBuf,uint16_t wPMABufAddr, uint16_t wNBytes);
void UserToPMABufferCopy(uint8_t *pbUsrBuf,uint16_t wPMABufAddr, uint16_t wNBytes);
三、USB-FS-Device Driver中间层
表6给出了USB-FS-Device Driver中间层模块:
1、usb_init(.h,.c)
此模块设置将在库中使用的初始化例程和全局变量。
2、usb_core (.h , .c)
这个模块是库的核心。它实现了usb2.0规范第9章中描述的所有功能。
可用的子例程涵盖了与控制端点(ENDP0)相关的USB标准请求的处理,提供了完成枚举阶段序列所需的代码。
实现状态机是为了处理设置事务的不同阶段。
USB核心模块还使用结构 User_Standard_Requests 在标准请求和用户实现之间实现动态接口。
当需要时,USB 内核就会将特定于类的请求和一些总线事件调度给用户程序。用户处理过程在 Device_Property 结构中给出。
内核使用的不同数据和函数结构将在以下段落中介绍。
1、设备表结构
核心模块将设备级信息保留在 Device_Table 结构中。Device_Table类型为:DEVICE。
typedef struct _DEVICE {
uint8_t Total_Endpoint;
uint8_t Total_Configuration;
} DEVICE;
2、设备信息结构
USB核心在 Device_Info 结构中保留来自主机的 setup 数据包,用于实现USB设备。这个结构的类型是:DEVICE INFO。
typedef struct _DEVICE_INFO {
uint8_t USBbmRequestType;
uint8_t USBbRequest;
uint16_t_uint8_t USBwValues;
uint16_t_uint8_t USBwIndexs;
uint16_t_uint8_t USBwLengths;
uint8_t ControlState;
uint8_t Current_Feature;
uint8_t Current_Configuration;
uint8_t Current_Interface;
uint8_t Current_AlternateSetting;
ENDPOINT_INFO Ctrl_Info;
} DEVICE_INFO;
union uint16_t_uint8_t 被定义为在 DEVICE INFO 中很容易地访问某些字段的 uint16_t t或 uint8_t 格式。
typedef union {
uint16_t w;
struct BW {
uint8_t bb1;
uint8_t bb0;
} bw;
} uint16_t_uint8_t;
结构字段的说明:
- USBbmRequestType 是 setup 数据包的 bmRequestType 的副本;
- USBbRequest 是 bRequest 的 setup 数据包的副本;
- USBwValues 被定义为类型:uint16_t_uint8_t,可以通过 3 个宏访问:
#define USBwValue USBwValues.w
#define USBwValue0 USBwValues.bw.bb0
#define USBwValue1 USBwValues.bw.bb1
// USBwValue 是 setup 数据包的 wValue 的副本
// USBwValue0 是 wValue 的低字节,USBwValue1 是 wValue 的高字节。
- USBwIndexs 被定义为 USBwValues,可以通过 3 个宏访问:
#define USBwIndex USBwIndexs.w
#define USBwIndex0 USBwIndexs.bw.bb0
#define USBwIndex1 USBwIndexs.bw.bb1
// USBwIndex 是 setup 数据包的 wIndex 的副本
// USBwIndex0 是 wIndex 的低字节,USBwIndex1 是 wIndex 的高字节。
- USBwLengths 被定义为类型:uint16_t_uint8_t,可以通过 3 个宏访问:
#define USBwLength USBwLengths.w
#define USBwLength0 USBwLengths.bw.bb0
#define USBwLength1 USBwLengths.bw.bb1
// USBwLength是一个 setup 数据包的wLength的副本
// USBwLength0 和 USBwLength1 分别是 wLength 的低字节和高字节。
- ControlState 是核心的状态,可用值在CONTROL_STATE中定义。
- Current_Feature 是设备功能在任何时候。它受 SET_FEATURE 和 CLEAR_FEATURE 请求的影响,并由 GET_STATUS 请求检索。用户代码不使用此字段。
- Current_Configuration 是设备随时处理的配置。它分别由SET_CONFIGURATION和GET_CONFIGURATION请求设置和检索。
- Current_Interface 是所选的接口。
- Current_Alternatesetting 是为当前工作配置和接口选择的替代设置。它分别由SET_INTERFACE和GET_INTERFACE请求设置和检索。
- Ctrl_Info 的类型 ENDPOINT_INFO。由于此结构在库中随处使用,因此定义了全局变量 pInformation 以便于访问Device_Info表,它是指向DEVICE_INFO结构的指针。实际上,pInformation = &Device_Info。
3、设备属性结构
当需要时,USBcore 将控制权分配给用户程序。
typedef struct _DEVICE_PROP {
void (*Init)(void);
void (*Reset)(void);
void (*Process_Status_IN)(void);
void (*Process_Status_OUT)(void);
RESULT (*Class_Data_Setup)(uint8_t RequestNo);
RESULT (*Class_NoData_Setup)(uint8_t RequestNo);
RESULT (*Class_Get_Interface_Setting)(uint8_t Interface,uint8_t AlternateSetting);
uint8_t* (*GetDeviceDescriptor)(uint16_t Length);
uint8_t* (*GetConfigDescriptor)(uint16_t Length);
uint8_t* (*GetStringDescriptor)(uint16_t Length);
void* RxEP_buffer; /* This field is not used in current library version. It is kept only for compatibility with previous versions */
uint8_t MaxPacketSize;
} DEVICE_PROP;
4、用户标准请求结构
用户标准请求结构是用户代码和标准请求管理之间的接口。该结构具有以下类型:USER_STANDARD_REQUESTS:
typedef struct _USER_STANDARD_REQUESTS {
void(*User_GetConfiguration)(void);
void(*User_SetConfiguration)(void);
void(*User_GetInterface)(void);
void(*User_SetInterface)(void);
void(*User_GetStatus)(void);
void(*User_ClearFeature)(void);
void(*User_SetEndPointFeature)(void);
void(*User_SetDeviceFeature)(void);
void(*User_SetDeviceAddress)(void);
} USER_STANDARD_REQUESTS;
如果用户想在收到标准的USB设备请求后实现特定的代码,他必须使用这个结构中的相应函数。
应用程序开发人员必须实现具有 DEVICE_PROP、Device_Table和USER_STANDARD_REQUEST类型的三个结构,以便管理类请求和特定于应用程序的控件。
这些结构的不同领域在第3.3.4节:usb_type.h / usb_def.h中进行了描述。
3、usb_sil(.h, .c)
usb_sil 模块为 USB-FS_Device 外设实现了一个额外的抽象层。它提供了用于访问读取和写入操作的终结点的简单函数。
1、端点简化写入功能
可以通过以下函数执行对终结点的写入操作:
// EPNum:与写操作相关的IN端点数
// pBufferPointer:指向要写入IN端点的用户缓冲区的指针。
// wBufferSize:要写入IN端点的数据字节数。
void USB_SIL_Write(uint32_t EPNum, uint8_t* pBufferPointer, uint32_t wBufferSize);
根据外设接口,此函数获取端点缓冲区的地址并执行数据包写入操作。
2、端点简化读取功能
可以通过以下函数执行来自端点的读取操作:
// EPNum:与写操作相关的IN端点数
// pBufferPointer:指向用户缓冲区的指针,该缓冲区将被从OUT端点读取的数据填充。
// 返回值:要写入IN端点的数据字节数。
uint32_t USB_SIL_Read(uint32_t EPNum, uint8_t* pBufferPointer);
根据外设接口的不同,此函数执行两个连续操作:
1、获取从相关 OUT 端点上的主机接收到的数据数。
2、将接收到的数据从 USB 专用内存复制到 pBufferPointer 地址。
然后,该函数将接收到的数据字节数返回给用户应用程序。
4、usb_type.h / usb_def.h
这些文件提供了库中使用的主要类型和USB定义。
5、platform_config.h
此文件负责为每个评估板提供特定配置。应将此文件复制到应用程序文件夹,然后用户可以在该文件夹中进行配置。
四、应用程序接口
应用程序接口的模块作为模板提供,它们必须由应用程序开发人员为每个应用程序定制。表7显示了应用程序接口中使用的不同模块。
1、usb_conf(.h)
usb conf.h用于定制 usb 例程,并对设备进行如下配置:
1、定义要使用的端点数量(通过定义的EP_NUM)。
2、通过注释相对回调定义来启用终结点和事件回调例程的使用(即注释定义 EP1_IN_Callback 在终结点 1 上发生正确传输时启用和使用此函数,注释定义 INTR_SOFINTR_Callback 以便在发生 SOF 中断时使用和实现此函数…)。当使用回调时,应注释其在usb_conf.h 文件中的相对定义。然后,它应该在用户应用程序中使用相同的名称实现(无需声明回调函数原型,因为它已经在 usb_istr.h 文件中声明)。您可以使用文件 usb_conf.h 执行以下操作:
- 在 PMA 中配置 BTABLE 和所有端点地址(通过修改和/或添加相对地址定义:BTABLE_ADDRESS、ENDP0_RXADDR、ENDP0_TXADDR…)。
- 定义中断以通过中断掩码IMR_MSK启用它们。
2、usb_desc (.h, .c)
usb_desc.c文件应包含与应用程序相关的所有 USB 描述符。用户必须根据应用程序的属性和类设置这些描述符。
在"STM32 USB-FS_Device开发人员套件"的所有可用演示中,都有一个基于STM32设备唯一ID寄存器(12位)实现唯一序列号字符串描述符的例程。
序列号字符串描述符的默认值是STM32,在USB初始化期间,Get_SerialNum()函数读取设备唯一ID寄存器并设置序列号字符串描述符。
有关设备唯一 ID 寄存器的更多详细信息,请参阅表 4。
3、usb_prop (.h , .c)
usb_prop 模块用于实现 USB 内核使用的 Device_Property,Device_Table 和 USER_STANDARD_REQUEST 结构。
1、设备属性实现
设备属性结构字段如下所述:
- void Init(void):USB-FS_Device 外设的初始化过程。在应用程序开始时调用它一次以管理初始化过程。
- void Reset(void):USB外围设备的重置过程。当宏单元从总线接收到 RESET 信号时调用它。用户程序应该在这个过程中设置端点,以便设置默认控制端点并使其能够接收。
- void Process_Status_IN(void):回调过程,当一个阶段的一个状态结束时被调用。用户程序可以通过这个回调来控制执行类和应用程序相关的进程。
- void Process_Status_OUT(void):回调过程,它在状态输出阶段结束时被调用。与进程状态IN一样,用户程序可以在状态out阶段之后执行操作。
- RESULT (see note below) *(Class_Data_Setup)(uint8_t RequestNo):回调过程,它在一个类请求被识别时调用,这个请求需要一个数据阶段。核心无法处理此类请求。在这种情况下,用户程序有机会使用自定义过程来分析请求,准备数据并将数据传递到USB-FS_Device内核以便与主机交换。参数 RequestNo 指示请求编号。此函数的返回参数具有以下类型:RESULT。它向核心指示请求处理的结果。
- RESULT (*Class_NoData_Setup)(uint8_t RequestNo):回调程序,它在非标准设备请求被识别时调用,即不需要数据阶段。核心无法处理此类请求。用户程序有机会使用自定义过程来分析请求并采取行动。此函数的返回参数具有类型:RESULT。它向核心指示请求处理的结果。
- RESULT (*Class_GET_Interface_Setting)(uint8_t Interface, uint8_t AlternateSetting):此例程用于测试收到的设置接口标准请求。用户必须根据自己的实现验证"接口"和"备用设置",并在这两个字段中出现错误时返回USB_UNSUPPORT。
- uint8_t* GetDeviceDescriptor(uint16_t Length):核心获取设备描述符。
- uint8_t* GetConfigDescriptor(uint16_t Length):核心获取配置描述符。
- uint8_t* GetStringDescriptor(uint16_t Length):核心获取字符串描述符。
- uint16_t MaxPacketSize:设备默认控制端点的最大数据包大小。
RESULT类型如下所示:
typedef enum _RESULT {
USB_SUCCESS = 0, /* request process sucessfully */
USB_ERROR, /* error */
USB_UNSUPPORT, /* request not supported */
USB_NOT_READY /* The request process has not been finished,*/
/* endpoint will be NAK to further requests*/
} RESULT;
2、设备端点实现
结构字段的描述:
- Total_Endpoint是 USB 应用程序使用的终结点数。
- Total_Configuration是 USB 应用程序具有的配置数。
3、用户标准请求实现
此结构用于在收到所有标准请求(Get 描述符除外)后管理用户实现。此结构的字段包括:
- void (*User_GetConfiguration)(void):在收到"获取配置标准"请求后调用。
- void (*User_SetConfiguration)(void):在收到"设置配置标准"请求后调用。
- void (*User_GetInterface)(void):在收到"获取接口标准"请求后调用。
- void (*User_SetInterface)(void):在收到"设置接口标准"请求后调用。
- void (*User_GetStatus)(void):在收到"获取接口标准"请求后调用。
- void (*User_ClearFeature)(void):在收到"清除功能标准"请求后调用。
- void (*User_SetEndPointFeature)(void):在接收到"设置的功能标准"请求后调用(仅针对端点接收方)。
- void (*User_SetDeviceFeature)(void):在接收到"设置的功能标准"请求后调用(仅对设备接收方)。
- void (*User_SetDeviceAddress)(void):在收到"设置的地址标准"请求后调用。
4、usb_endp (.c)
USB_endp模块用于:
- 处理 USB-FS_Device 外设的端点 0 (EP0) 以外的端点的 CTR"正确传输"例程。
为了启用这些回调处理程序的处理,必须在 USB_conf.h 文件中定义名为 EPx_IN_Callback(用于 IN 传输)或EPx_OUT_Callback(用于 OUT 传输)或 EPx_RX_ISOC_CALLBACK(对于常时等量输出传输)的预处理器开关。
5、usb_istr(.c)
USB_istr 模块提供了一个名为 USB_Istr() 的函数来处理所有 USB 中断。
对于每个 USB 中断源,都提供了一个名为 XXX_Callback 的回调函数(例如,RESET_Callback),以便实现用户中断处理程序。为了能够处理每个回调函数,必须在USB配置文件USB_conf.h中定义一个名为XXX_callback的预处理器开关。
6、usb_pwr (.h , .c)
此模块管理 USB 设备的电源管理。它提供了表8所示的函数。
五、使用STM32 USB-FS-Device库实现USB-FS_Device应用程序
1、实现无数据类特定的请求
所有没有数据传输阶段的类特定请求都实现了结构设备属性的RESULT (Class NoData Setup)(uint8 t RequestNo)字段。请求的USBbRequest在RequestNo参数中可用,所有其他的请求字段都存储在设备信息结构中。
用户必须测试所有请求字段。如果请求与要实现的类兼容,则该函数将返回USB_SUCCESS结果。但是,如果请求中存在问题,该函数将返回 UNSUPPORT 结果状态,并且库将以 STALL 握手进行响应。
2、如何实现一个数据类特定的请求
如果类请求需要数据传输阶段,则用户实现向 USB-FS-Device 库报告要传输的数据的长度和内部存储器中的数据位置(如果从主机接收数据,则为 RAM;如果数据发送到主机,则为 RAM 或闪存)。这种类型的请求在函数中进行管理:
RESULT (*Class_Data_Setup)(uint8_t RequestNo);
对于每个类数据请求,用户必须使用以下格式创建一个特定函数:
uint8_t* My_First_Data_Request (uint16_t Length);
如果调用此函数时 Length 参数等于0,它将设置 pInformation->Ctrl_Info.Usb_wLength 字段,其中包含要传输的数据的长度,并返回 NULL 指针。在其他情况下,它返回要传输的数据的地址。下面的 C 代码显示了一个简单的示例:
uint8_t* My_First_Data_Request (uint16_t Length) {
if (Length == 0) {
pInformation->Ctrl_Info.Usb_wLength
return NULL;
} else
return (&My_Data_Buffer);
}
函数 RESULT (*Class_Data_Setup)(uint8_t RequestNo) 管理所有数据请求,如以下 C 代码中所述:
RESULT Class_Data_Setup(uint8_t RequestNo) {
uint8_t*(*CopyRoutine)(uint16_t);
CopyRoutine = NULL;
if (My_First_Condition)// test the filds of the first request
CopyRoutine = My_First_Data_Request;
else if(My_Second_Condition) // test the filds of the second request
CopyRoutine = My_Second_Data_Request;
/*
... same implementation for each class data requests
...
*/
if (CopyRoutine == NULL) return USB_UNSUPPORT;
pInformation->Ctrl_Info.CopyData = CopyRoutine;
pInformation->Ctrl_Info.Usb_wOffset = 0;
(*CopyRoutine)(0);
return USB_SUCCESS;
} /*End of Class_Data_Setup */
3、如何管理非控制端点的数据传输
使用非默认管道(Endpoint 0)的数据传输管理可以在 usb_end.c 文件中管理。
用户必须取消注释文件 usb_conf.h 中与端点(有方向)对应的行。