USB事务处理:
在 USB 协议中,USB 的数据传输由信息包组成,这些信息包组合
起来可以构成完整的事务处理。USB 事务处理是 USB 主机和 USB 功能
设备之间数据传输的基本单位。USB 的信息包和事务处理具有特定的
格式。
每个双向EP对应两个packet buffer,分别
用于发送和接收软件通过packet buffer
• 软件通过packet buffer interface来访问它们
• 这些packet buffer的位置和大小都可配置,由buffer描述表指定
• Buffer描述表本身也在这块memory里,它自己的地址是由USB_BTABLE寄存器指定的
• USB外设硬件不会把本EP的数据溢出到与其相邻的其他packet
usb_prop.c中:CustomHID_Reset(void)中
端点(EP)资源:
有8个双向端点
• 双向EP0是必备的
• 其他EPi,应用可同时使用收、发两个方向或只使用一个方向
各device demo对EP的使用情况:
1. 应用中使用到的EP尽量内部编号考前(0~7),这样可以使得【硬件缓冲描述表】 尽可能小
2. 库函数默认把EPx的地址设置成x
SetDeviceAddress(Val) @ <usb_core.c>
uint32_t nEP = Device_Table.Total_Endpoint;
for (i = 0; i < nEP; i++)
{
_SetEPAddress((uint8_t)i, (uint8_t)i);
}
_SetDADDR(Val | DADDR_EF); 设置设备地址的同时使能USB模块
IN packet 的处理:
OUT/SETUP packet的处理:
Control transfer的处理 :
双缓冲端点:
分配了一个结构体(Device_Info)来记录收到的主机命令,以及设备当前状态;并用一个指针(plnformation)来指向它
//USB驱动将主机发送过来的USB设备的设置包保存在设备信息结构表中
typedef struct _DEVICE_INFO
{
u8 USBbmRequestType; /* bmRequestType */
u8 USBbRequest; /* bRequest */
u16_u8 USBwValues; /* wValue */
u16_u8 USBwIndexs; /* wIndex */
u16_u8 USBwLengths; /* wLength */
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;/* Selected Alternate Setting of current
interface*/
ENDPOINT_INFO Ctrl_Info;
}DEVICE_INFO;
typedef struct _ENDPOINT_INFO
{
/*当从设备发送数据时:copydata()可获取数据缓冲区的长度,如果长度为0,copydata()返回数据的总长度如果不支持请求,则返回0,如果返回-1,停止进行下一步,初始化,如果不是0,返回数据指针*/
u16 Usb_wLength;//待发送数据
u16 Usb_wOffset;//原始数据偏移量
u16 PacketSize;
u8 *(*CopyData)(u16 Length);
}ENDPOINT_INFO;
状态表示:
typedef enum _CONTROL_STATE
{
WAIT_SETUP, /* 0 */
SETTING_UP, /* 1 */
IN_DATA, /* 2 */
OUT_DATA, /* 3 */
LAST_IN_DATA, /* 4 */
LAST_OUT_DATA, /* 5 */
WAIT_STATUS_IN, /* 7 */
WAIT_STATUS_OUT, /* 8 */
STALLED, /* 9 */
PAUSE /* 10 */
} CONTROL_STATE; /* The state machine states of a control pipe */
//USB驱动将主机发送过来的USB设备的设置包保存在设备信息结构表中
typedef struct _DEVICE_INFO
{
u8 USBbmRequestType; /* bmRequestType */
u8 USBbRequest; /* bRequest */
u16_u8 USBwValues; /* wValue */
u16_u8 USBwIndexs; /* wIndex */
u16_u8 USBwLengths; /* wLength */
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;/* Selected Alternate Setting of current
interface*/
ENDPOINT_INFO Ctrl_Info;
}DEVICE_INFO;
处理主机的setup:
*处理主机的setup请求 80 06 00 01 00 00 40 00
*******************************************************************************/
u8 Setup0_Process(void)
{
union
{
u8* b;
u16* w;
} pBuf;
//取的端点0的接收缓冲区地址
pBuf.b = PMAAddr + (u8 *)(_GetEPRxAddr(ENDP0) * 2); /* *2 for 32 bits addr */
if (pInformation->ControlState != PAUSE)
{
pInformation->USBbmRequestType = *pBuf.b++; /* 请求类型,标明方向和接收对象(设备、接口还是端点),80:设备到主机 */
pInformation->USBbRequest = *pBuf.b++; /* 请求代码:首次为6:标明主机要获取设备描述符 */
pBuf.w++; /* word not accessed because of 32 bits addressing */
pInformation->USBwValue = ByteSwap(*pBuf.w++); /* 标明是啥请求 */
pBuf.w++; /* word not accessed because of 32 bits addressing */
pInformation->USBwIndex = ByteSwap(*pBuf.w++); /* 用来说明端点号或 */
pBuf.w++; /* word not accessed because of 32 bits addressing */
pInformation->USBwLength = *pBuf.w; /* 该请求应答的长度 */
}
pInformation->ControlState = SETTING_UP;
if (pInformation->USBwLength == 0)
{
/* 第2阶段: */
NoData_Setup0();
}
else
{
/* 第1阶段,交互设备描述符。交互完,主机复位USB,进入第2阶段*/
Data_Setup0();
}
return Post0_Process();
}
//完成描述符的输出准备
void DataStageIn(void)
{
...
DataBuffer = (*pEPinfo->CopyData)(Length);//用户描述符缓冲区地址,18字节
UserToPMABufferCopy(DataBuffer, GetEPTxAddr(ENDP0), Length);//将描述符复制到发送缓冲区
SetEPTxCount(ENDP0, Length);//设置发送长度
pEPinfo->Usb_wLength -= Length;
pEPinfo->Usb_wOffset += Length;
vSetEPTxStatus(EP_TX_VALID);//使能端点发送,主要主机的令牌包一来,就启动发送
USB_StatusOut();/* USB接收有效 */
Expect_Status_Out:
pInformation->ControlState = ControlState;//进入第2阶段
}
DataStageOut
结构体初始化二
分配了一个结构体(Device_Property)来定义该设备的各个回掉函数;
并用一个指针(pProperty)来指向它
DEVICE_PROP Device_Property =
{
CustomHID_init,
CustomHID_Reset,
CustomHID_Status_In,
CustomHID_Status_Out,
CustomHID_Data_Setup,
CustomHID_NoData_Setup,
CustomHID_Get_Interface_Setting,
CustomHID_GetDeviceDescriptor,
CustomHID_GetConfigDescriptor,
CustomHID_GetStringDescriptor,
0,
0x40 /*MAX PACKET SIZE*/
};
结构体定义如下:
void (*Init)(void); /* Initialize the device */
void (*Reset)(void); /* Reset routine of this device */
/* Device dependent process after the status stage */
void (*Process_Status_IN)(void);
void (*Process_Status_OUT)(void);
RESULT (*Class_Data_Setup)(u8 RequestNo);
RESULT (*Class_NoData_Setup)(u8 RequestNo);
RESULT (*Class_Get_Interface_Setting)(u8 Interface, u8 AlternateSetting);
u8* (*GetDeviceDescriptor)(u16 Length);
u8* (*GetConfigDescriptor)(u16 Length);
u8* (*GetStringDescriptor)(u16 Length);
u8* RxEP_buffer;
u8 MaxPacketSize;
结构体初始化三、
User_Standard_Requests
分配了一个结构体(User_Standard_Requests)来定义该设备用来响应主机标准命令时的各种回调函数; 并用一个指针(pUser_Standard_Requests) 来指向它
初始化:
void USB_Init(void)
看一下设备本身的属性和方法:
• 获取序列号
根据该芯片的Unique ID来更新“序号字符串”(MASS_StringSerial[26])
• 连接设备: PowerOn()
• 拉低PB14来使能USB_D+线上的上拉电阻
• 置位并复位FRES@USB_CNTR来对USB外设进行复位
• 复位ISTR所有位来清除可能pending的中断
• 设置本应用关心的中断事件: RESET、 SUSP、 WKUP
首先分析下RESET中断:
首次连接主机,主机会下发从机复位
• EP0:控制类型;发送NAK、接收Valid;设置接收buffer地址和长度;设置发送buffer地址
• EP1:批量类型;发送NAK、接收Disable;设置发送buffer地址
• EP2:批量类型;发送Disable、接收VALID;设置接收buffer地址和长度
• 硬件置位表示一个transaction正确完成了
• 软件需要根据EP_ID和DIR来得知这次transaction是发生在哪个EP上的何种
transaction(SETUP/OUT/IN transaction?)
1、extract highest priority endpoint number
(wIstr = _GetISTR());
EPindex = (u8)(wIstr & ISTR_EP_ID);
端口号为0时:
/* save RX & TX status */
/* and set both to NAK */
参看传输方向
if((wIstr&ISTR_DIR)==0)//方向为IN,主机发送完成
{
//首先清除CTR_TX
_ClearEP_CTR_TX(ENDP0);
In0_Process();
//恢复EP0的RX和TX的状态
_SetEPRxStatus(ENDP0, SaveRState);
_SetEPTxStatus(ENDP0, SaveTState);
}
else//接收,分为setup接收和数据接收
{
。。。
}
pInformation->ControlState = SETTING_UP;
//函数指针参数为16位参数,返回值是指向8位变量的指针
u8 *(*CopyRoutine)(u16);
【作用和意义】 通过这个函数获得用户的数据缓冲区地址,从而可以在OUT过程中把收到的数据拷贝到用户缓冲区,或在IN过程中把用户缓冲区的数据拷贝到USB发送缓冲区
OUT过程,多个DATA_OUT传输,库需要多次调用回调函数CopyRoutine,返回将要容纳已经收在硬件buf中数据的缓 冲区指针
IN过程,多个DATA_IN传输, 每次需要向主机传输数据时, USB库都会调用一次回调函数CopyRoutine,它返回一 个包含要发送的数据的缓冲区指针,库再把这个缓冲区的内容拷贝的硬件buf以择机发送出去
完成描述符的输出准备:
ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info;