目录
4、类方法结构体USBD_ClassTypeDef(*pClass)
以CDC类举例,首先看一张图(原谅这张图吧,实在是不想画visio了,能看懂就行).
其中USBD_DescriptorsTypeDef因为只涉及到枚举过程中的设备描述符和字符串描述符,也没什么特别需要注意的,一般情况下,是不会修改它的,所以也就没写.
1、重要的状态类型
1.1、USBD_StatusTypeDef
几乎所有库函数都会返回类型为 USBD_StatusTypeDef 的状态,应用应该始终检查返回的状态。定义如下.
typedef enum
{
USBD_OK = 0U,
USBD_BUSY,
USBD_FAIL,
} USBD_StatusTypeDef;
2.1、 EP0 Statue
定义了EP0端点的几个状态.保存在USBD_HandleTypeDef的ep0_state变量中.如下
/* 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
2、设备内核结构体
在DEVICE库里面,最主要的数据结构是USBD_HandleTypeDef,它将USB传输中的数据抽象到该数据结构内.该结构体包含所有变量和结构体,用以实时保存与设备、控制传输状态机以及端点的状态相关的所有信息.USB内核层所有的操作都围绕着该数据结构展开.
下面列出了其中比较重要的成员变量,未列出的基本未使用或者很少使用.
成员变量 | 描述 |
---|---|
dev_config | 枚举过程的最后会有一个SET_CONFIG的通用标准请求,该变量保存该请求内设置的配置值. |
ep_in[16] | STM32全系列最多由16个端点,可以全部作为单向端点,为了兼容所以定义了16个端点.该变量保存了端点的最大包长,发送/接收数据总长度,剩余发送/接收总长度,是否被使用,端点状态等属性. |
ep_out[16] | 同上 |
ep0_state | EP0端点的状态,它控制着内核的状态机 |
dev_state | 设备库里只支持连接、配置和上电 其余的统统认为时默认状态 |
dev_address | 保存主机位设备分配的地址 |
request | 标准请求存放 |
*pDesc | 指向设备描述符,字符串描述符的管理接口结构体 |
*pClass | 指向USB Device Class的操作接口,一般存放在USBD_XXX.C(XXX代表类名)文件. |
*pClassData | 指向USB Device Class的数据接口,初始化时候动态内存分配的,一般存放在USBD_XXX.H(XXX代表类名)文件 |
*pUserData | 指向用户自己实现的应用层的函数集合,一般存放在USBD_XXX_IF.C文件 |
*pData | 指向底层硬件驱动结构体,根据硬件平台也不一样. |
3、USB驱动结构体(*pData)
硬件相关的结构体PCD_HandleTypeDef,根据具体的硬件平台不一样,可能略有变化.
成员变量 | 描述 |
*Instance | USB_IP Peripheral Registers base address |
Init | cubemx配置页面的参数 |
USB_Address | 主机为设备分配的USB地址 |
IN_ep[8] | F1系列只有8个双向端点,所以定义为8. 存放着端点的端点号,方向,类型,缓存地址等等很多东西. |
OUT_ep[8] | 同上 |
Setup[12] | 标准请求的数据,硬件接受完就存放到这了. |
*pData | 指向了设备内核结构体. |
![]() | 没有使用,各个阶段的回调函数,如有需要可打开宏后,自行实现. |
4、类方法结构体USBD_ClassTypeDef(*pClass)
USB协议栈将所有USB类都抽象成一个数据结构:USBD_ClassTypeDef,其定义如下所示:
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;
ST目前官方提供的USB设备类的DEMO程序并没有囊括所有USB类,因此,若用户需要实现这些官方提供DEMO之外的USB类时,则用户需要根据自己的需要来定制化自己的USB类。
这个结构体是一个抽象类,定义了一些虚拟函数,比如初始化,反初始化,类请求指令处理函数,端点0发送完成,端点0接收处理,数据发送完成,数据接收处理,SOF中断处理,同步传输发送未完成,同步传输接收未完成处理等等;用户在实现自己具体的USB类的时候需要将它实例化,USBD_ClassTypeDef结构体是USBD内核提供给外部定义一个USB设备类的窗口,而USB类文件(如usbd_cdc.c)实际就是实现这个结构体具体实例化的过程。最后通过USBD_HandleTypeDef的pClass进行链接.
在ST提供的CDC的DEMO如下.
USBD_ClassTypeDef USBD_CDC =
{
USBD_CDC_Init,
USBD_CDC_DeInit,
USBD_CDC_Setup,
NULL, /* EP0_TxSent, */
USBD_CDC_EP0_RxReady,
USBD_CDC_DataIn,
USBD_CDC_DataOut,
NULL,
NULL,
NULL,
USBD_CDC_GetHSCfgDesc,
USBD_CDC_GetFSCfgDesc,
USBD_CDC_GetOtherSpeedCfgDesc,
USBD_CDC_GetDeviceQualifierDescriptor,
};
这些成员函数都在USBD_CDC.C文件中实现,除此之外,还有对上层应用提供的接口,比如让内核调用CDC类方法的接口,CDC类的发送等API.
/**/
uint8_t USBD_CDC_SetTxBuffer(USBD_HandleTypeDef *pdev,
uint8_t *pbuff,
uint16_t length);
uint8_t USBD_CDC_SetRxBuffer(USBD_HandleTypeDef *pdev,
uint8_t *pbuff);
/*--------------------------------对上层应用提供的接口API----------------------------------*/
uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev);
uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev);
uint8_t USBD_CDC_RegisterInterface(USBD_HandleTypeDef *pdev,
USBD_CDC_ItfTypeDef *fops);
5、类特有的数据结构(*pClassData)
依旧以CDC类举例,因为CDC是通信类,所以数据结构中少不了的会有发送接收缓冲区的地址,长度等信息,官方提供的demo中定义的结构体如下所示.
typedef struct
{
uint32_t data[CDC_DATA_HS_MAX_PACKET_SIZE / 4U]; //USB的CDC类标准请求全部在该数组存放
uint8_t CmdOpCode; //CDC类的bRequest值
uint8_t CmdLength; //该次请求指令的数据长度
uint8_t *RxBuffer;
uint8_t *TxBuffer;
uint32_t RxLength;
uint32_t TxLength;
__IO uint32_t TxState;
__IO uint32_t RxState;
}
USBD_CDC_HandleTypeDef;
需要注意的是该结构体的初始化实在USBD_ClassTypeDef的初始化里面,在官方提供的DEMO里面使用的是一个假的动态内存分配,如下所示.
void *USBD_static_malloc(uint32_t size)
{
static uint32_t mem[(sizeof(USBD_CDC_HandleTypeDef)/4)+1];/* On 32-bit boundary */
return mem;
}
/** Alias for memory allocation. */
#define USBD_malloc (uint32_t *)USBD_static_malloc
/** Alias for memory release. */
#define USBD_free USBD_static_free
static uint8_t USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
/*省略代码*/
pdev->pClassData = USBD_malloc(sizeof(USBD_CDC_HandleTypeDef));
/*省略代码*/
}
6、类的用户方法结构体(*pUserData)
依旧以CDC类为例子,在USBD_CDC_IF.H文件中提供了如下结构体
typedef struct _USBD_CDC_Itf
{
int8_t (* Init)(void);
int8_t (* DeInit)(void);
int8_t (* Control)(uint8_t cmd, uint8_t *pbuf, uint16_t length);
int8_t (* Receive)(uint8_t *Buf, uint32_t *Len);
} USBD_CDC_ItfTypeDef;
该类结构体存在的意义就是为了让USBD_CDC类与应从层代码完全分离,类似USBD内核与USBD_CDC类完全分离一般.
7、综述
综上,基本上重要的结构体已经说完了,最终的关系如下所示.
备注: 做好的表格提交的时候飞了??? 还要重新写,心态炸了啊