<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
二、USB函数库分析
3、usb_mem.h和usb_mem.c
(1)usb_mem.h
这个头文件很简单,只声明了两个函数,供其它源文件使用。
void UserToPMABufferCopy(u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes); 用户数据复制到端点对应的USB包缓冲区。
void PMAToUserBufferCopy(u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes);
(2)usb_mem.c
上述两个函数的实现。我这里将详细分析一个函数的代码。
void UserToPMABufferCopy(
u8 *pbUsrBuf, //用户提供的数据缓冲区指针。
u16 wPMABufAddr, //端点地址,这是包缓冲区内相对地址
u16 wNBytes) //本次复制的字节数
{
u32 n = (wNBytes + 1) >> 1; /*用户提供的是字节个数,而包缓冲区每次处理一个字、两个字节,这里是进行转换,+1的目的是防止用户给出奇数。比如用户参数为9,则实际需要5个字。 */
u32 i, temp1, temp2;
u16 *pdwVal;
pdwVal = (u16 *)(wPMABufAddr * 2 + PMAAddr); // wPMABufAddr实际是端点描述符表寄存器里对应的相对地址,两个字节对齐。而stm32内部实际是4个字节对齐。举个例子:端点0描述附表,在包缓冲区相对偏移0起始,占据8字节,分别为“0018,0008,0058,<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />0000”。第0-1字节存储发送缓冲区的相对地址,第4-5字节存储接收缓冲区的相对地址。
比如现在要将接收缓冲区的内容复制到用户缓冲区,首先要取得这个“0058”数值,获取的方法是 缓冲区相对偏移RxADDR =“0058” = *(u16*)(包缓冲区起始地址 + 4*2)。这里4*2的意思是“0058”的相对偏移实际是第8个字节,而不是相对的偏移4字节。
然后获取接收缓冲区实际地址。 缓冲区实际地址 = 缓冲区起始地址 + “0058”*2。这里乘以2的意思跟上述是一样的。
for (i = n; i != 0; i--)
{
temp1 = (u16) * pbUsrBuf; //取得低字节
pbUsrBuf++; //指向高字节
temp2 = temp1 | (u16) * pbUsrBuf << 8; //高低组合成一个字
*pdwVal++ = temp2; //写入包缓冲区的发送缓冲区。
pdwVal++; //四字节对齐,跳过两个字节
pbUsrBuf++; //指向下一个字节。
}
}
4、usb_init.c和usb_init.h
(1)usb_init.h
主要是声明了函数ust_init()。
声明了一些外部变量。
extern USER_STANDARD_REQUESTS *pUser_Standard_Requests;
//有几个很重要的指针,在这个文件里作外部声明。
extern u16 SaveState ;
extern u16 wInterrupt_Mask;
(3)usb_init.c
DEVICE_INFO Device_Info;//这个最重要的设备信息机构体在这里定义。
函数实现:
void USB_Init(void)
{
pInformation = &Device_Info; // pInformation是在usb控制传输处理核心usb_core.c中最重要的指针。
pInformation->ControlState = 2; //2实际上代表IN_DATA状态,实际我觉得设置为0最好,代表WAIT_SETUP状态。
pProperty = &Device_Property; //这个结构体由用户提供,是一个函数指针结构体,大部分成员都是上层协议需要实现的操作函数。
pUser_Standard_Requests = &User_Standard_Requests; //这个结构体跟上一个类似,主要是用户对标准请求的实现。
/* Initialize devices one by one */
pProperty->Init(); //调用用户提供的初始化函数。
}
5、usb_int.c和usb_int.h
(1)usb_int.h
主要是声明了函数void CTR_LP(void),这是数据传输完成处理的核心处理部分。
(2)usb_int.c
这个文件主要是实现了函数void CTR_LP(void)。
开头:
u16 SaveRState;
u16 SaveTState; //这是中断处理时用于保存当时端点的状态
extern void (*pEpInt_IN[7])(void); /* Handles IN interrupts */
extern void (*pEpInt_OUT[7])(void); /* Handles OUT interrupts */
这两个函数指针数组的定义在用户层usb_prop.c文件中。
void CTR_LP(void)
{
u32 wEPVal = 0;
while (((wIstr = _GetISTR()) & ISTR_CTR) != 0)
{
_SetISTR((u16)CLR_CTR); /* clear CTR flag */
EPindex = (u8)(wIstr & ISTR_EP_ID); //获取发生中断的端点号,分成0和非0端点两种情况。
if (EPindex == 0)
{
SaveRState = _GetEPRxStatus(ENDP0);
SaveTState = _GetEPTxStatus(ENDP0);
_SetEPRxStatus(ENDP0, EP_RX_NAK);
_SetEPTxStatus(ENDP0, EP_TX_NAK);
if ((wIstr & ISTR_DIR) == 0)
{
_ClearEP_CTR_TX(ENDP0);
In0_Process(); //取得方向标志,如果为0表示主机要“IN”数据。调用In0_Process()来处理。
_SetEPRxStatus(ENDP0, SaveRState);
_SetEPTxStatus(ENDP0, SaveTState);
return;
}
Else //DIR==1,有两种情况,可能是主要要“OUT”,也可能是在“SETUP”。
{
wEPVal = _GetENDPOINT(ENDP0);
if ((wEPVal &EP_SETUP) != 0) //SETUP的情况
{
_ClearEP_CTR_RX(ENDP0);
Setup0_Process(); //调用这个函数处理控制传输过程,这个函数的理解是理解USB工作最关键的。大部分usb_core.c文件里面的函数都是为它服务的。
_SetEPRxStatus(ENDP0, SaveRState);
_SetEPTxStatus(ENDP0, SaveTState);
return;
}
else if ((wEPVal & EP_CTR_RX) != 0)//OUT的情况
{
_ClearEP_CTR_RX(ENDP0);
Out0_Process(); //调用这个函数处理用户输出。
_SetEPRxStatus(ENDP0, SaveRState);
_SetEPTxStatus(ENDP0, SaveTState);
return;
}
}
}/* if(EPindex == 0) */
Else //这是非0端点的处理。在JoyStickMouse例程中几乎没什么作用。就是一个空架子。
{
wEPVal = _GetENDPOINT(EPindex);
if ((wEPVal & EP_CTR_RX) != 0)
{
_ClearEP_CTR_RX(EPindex);
(*pEpInt_OUT[EPindex-1])();
} /* if((wEPVal & EP_CTR_RX) */
if ((wEPVal & EP_CTR_TX) != 0)
{
_ClearEP_CTR_TX(EPindex);
(*pEpInt_IN[EPindex-1])();
} /* if((wEPVal & EP_CTR_TX) != 0) */
}/* if(EPindex == 0) else */
}/* while(...) */
}
6、usb_def.h
主要是定义了与USB标志请求相关的一些常量。
typedef enum _RECIPIENT_TYPE
{
DEVICE_RECIPIENT, /* Recipient device */
INTERFACE_RECIPIENT, /* Recipient interface */
ENDPOINT_RECIPIENT, /* Recipient endpoint */
OTHER_RECIPIENT
} RECIPIENT_TYPE;//请求发往的对象,可以是设备、接口、端点以及其它(这个需要用户特别实现)。
typedef enum _STANDARD_REQUESTS
{
GET_STATUS = 0, //这个可以发给设备、接口和端点。
CLEAR_FEATURE, //这个可以发给设备、接口和端点
RESERVED1,
SET_FEATURE, //这个可以发给设备、接口和端点
RESERVED2,
SET_ADDRESS,
GET_DESCRIPTOR,//其余的全部都是发往设备处理。
SET_DESCRIPTOR,
GET_CONFIGURATION,
SET_CONFIGURATION,
GET_INTERFACE,//这个发往接口
SET_INTERFACE,//这个也是发往接口。
TOTAL_sREQUEST, /* Total number of Standard request */
SYNCH_FRAME = 12
} STANDARD_REQUESTS; //标志请求总共13种,USB实现11种。
typedef enum _DESCRIPTOR_TYPE
{
DEVICE_DESCRIPTOR = 1,
CONFIG_DESCRIPTOR,
STRING_DESCRIPTOR,
INTERFACE_DESCRIPTOR,
ENDPOINT_DESCRIPTOR
} DESCRIPTOR_TYPE; //描述符类型,这里都是标志描述符,类特殊描述符由用户程序支持。
typedef enum _FEATURE_SELECTOR
{
ENDPOINT_STALL,
DEVICE_REMOTE_WAKEUP
} FEATURE_SELECTOR;//这个是可以设置的特性,Get_Feature()和Set_Feature()请求所用。
#define REQUEST_TYPE 0x60 /* 请求发送方屏蔽位,可能是标准请求、类请求和厂商请求。 */
#define STANDARD_REQUEST 0x00 /* Standard request */
#define CLASS_REQUEST 0x20 /* Class request */
#define VENDOR_REQUEST 0x40 /* Vendor request */
#define RECIPIENT 0x1F /* 请求接收对象的屏蔽位。 */
7、usb_core.h
一些核心处理结构体就是在这个头文件中定义的。
typedef enum _CONTROL_STATE
{
WAIT_SETUP, /* 0 */初始状态是等待SETUP
SETTING_UP, /* 1 */收到SETUP后是建立中。
IN_DATA, /* 2 */
OUT_DATA, /* 3 */
LAST_IN_DATA, /* 4 */
LAST_OUT_DATA, /* 5 */
WAIT_STATUS_IN, /* 7 */数据阶段是OUT,则状态阶段主机发IN。
WAIT_STATUS_OUT, /* 8 */
STALLED, /* 9 */用户请求不支持,等待下一个SETUP。
PAUSE /* 10 */这个可能表示出错了。
} CONTROL_STATE; /* 控制传输阶段对主机信号的处理,是以状态机类似的处理方式,这个就是设备可能的状态。*/
typedef struct _ENDPOINT_INFO
{//端点传输信息结构体,前面本来有一段很长的英文注释,重点讲解CopyData的作用。实际它不是用于复制的,主要是指明数据长度、返回数据缓冲指针的。
u16 Usb_wLength; //还有多少数据需要操作。
u16 Usb_wOffset; //当前数据缓冲的偏移
u16 PacketSize; //端点支持的包长度。
u8 *(*CopyData)(u16 Length); //这是一个函数指针定义。
}ENDPOINT_INFO;
typedef struct _DEVICE_INFO
{
u8 USBbmRequestType; /* bmRequestType */
u8 USBbRequest; /* bRequest */
u16_u8 USBwValues; /* wValue */
u16_u8 USBwIndexs; /* wIndex */
u16_u8 USBwLengths; /* wLength */
以上部分从SETUP包后面跟的数据包中获得。
u8 ControlState; /* of type CONTROL_STATE */
u8 Current_Feature;
u8 Current_Configuration; /* Selected configuration */
u8 Current_Interface; /* Selected interface of current configuration
u8 Current_AlternateSetting;/ */
ENDPOINT_INFO Ctrl_Info;
}DEVICE_INFO;
这个就是控制传输最重要的结构体定义。
typedef struct _DEVICE_PROP
{
void (*Init)(void); /* Initialize the device */
void (*Reset)(void); /* Reset routine of this device */
void (*Process_Status_IN)(void);
void (*Process_Status_OUT)(void);//用户对状态过程的特殊处理
RESULT (*Class_Data_Setup)(u8 RequestNo);
RESULT (*Class_NoData_Setup)(u8 RequestNo); //有些类特殊请求,usb_core.c并不会支持,而交由上层用户处理。
RESULT(*Class_Get_Interface_Setting)(u8 Interface,u8 lternateSetting);
u8* (*GetDeviceDescriptor)(u16 Length);
u8* (*GetConfigDescriptor)(u16 Length);
u8* (*GetStringDescriptor)(u16 Length); //描述符都是与具体功能设备相关的,上层用户要提供给底层“usb_core.c”统一的操作模式。
u8* RxEP_buffer;
u8 MaxPacketSize;
}DEVICE_PROP;
对四个最重要函数的声明:
u8 Setup0_Process(void);
u8 Post0_Process(void);
u8 Out0_Process(void);
u8 In0_Process(void);
对四个最重要结构体的声明:
extern DEVICE_PROP Device_Property;
extern USER_STANDARD_REQUESTS User_Standard_Requests;
extern DEVICE Device_Table;
extern DEVICE_INFO Device_Info;
这四个结构体前三个由用户定义和实现,第四个结构体由“usb_init.c”定义和初始化,主要在“usb_core.c”中起作用。
今天的分析就先到这儿,明天再详细分析一下“usb_core.c”。
文章评论(0条评论)
登录后参与讨论