• 首页
  • 中国
  • USB二:深入解析STM32_USB-FS-Device_Lib 库

USB二:深入解析STM32_USB-FS-Device_Lib 库

2023-11-01 103浏览
百检网是一家专业的第三方检测平台,汇聚众多拥有权威资质的第三方检测机构为你提供一站式的检测服务,做检测就上百检网。百检网让检测从此检测,一份报告全国通用,专业值得信赖。

USB事务处理:

在 USB 协议中,USB 的数据传输由信息包组成,这些信息包组合

起来可以构成完整的事务处理。USB 事务处理是 USB 主机和 USB 功能

设备之间数据传输的基本单位。USB 的信息包和事务处理具有特定的

格式。

Packet buff的使用

每个双向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)的初始化

首次连接主机,主机会下发从机复位

端点(EP)资源:

有8个双向端点

• 双向EP0是必备的

• 其他EPi,应用可同时使用收、发两个方向或只使用一个方向

各device demo对EP的使用情况:

1. 应用中使用到的EP尽量内部编号考前(0~7),这样可以使得【硬件缓冲描述表】 尽可能小

2. 库函数默认把EPx的地址设置成x

SetDeviceAddress(Val) @

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)

看一下设备本身的属性和方法:

看一下初始化的过程:

pProperty->Init();

• 获取序列号

根据该芯片的Unique ID来更新“序号字符串”(MASS_StringSerial[26])

• 连接设备: PowerOn()

• 拉低PB14来使能USB_D+线上的上拉电阻

• 置位并复位FRES@USB_CNTR来对USB外设进行复位

• 复位ISTR所有位来清除可能pending的中断

• 设置本应用关心的中断事件: RESET、 SUSP、 WKUP

整个USB通信是由中断驱动的

不同应用关心不同的中断事件:

**分析下RESET中断:

首次连接主机,主机会下发从机复位

1、 CustomHID_Reset

• 初始化EP

• EP0:控制类型;发送NAK、接收Valid;设置接收buffer地址和长度;设置发送buffer地址

• EP1:批量类型;发送NAK、接收Disable;设置发送buffer地址

• EP2:批量类型;发送Disable、接收VALID;设置接收buffer地址和长度

设置状态标志:

2、CTR中断

• 硬件置位表示一个transaction正确完成了

• 软件需要根据EP_ID和DIR来得知这次transaction是发生在哪个EP上的何种

transaction(SETUP/OUT/IN transaction?)

void CTR_LP(void)

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接收和数据接收

{

。。。

}

Setup0_Process()

**步:从EP0的接收Packet buf中读取主机命令

pInformation->ControlState = SETTING_UP;

第二步:

交互设备描述符:

//函数指针参数为16位参数,返回值是指向8位变量的指针

u8 *(*CopyRoutine)(u16);

【作用和意义】 通过这个函数获得用户的数据缓冲区地址,从而可以在OUT过程中把收到的数据拷贝到用户缓冲区,或在IN过程中把用户缓冲区的数据拷贝到USB发送缓冲区

OUT过程,多个DATA_OUT传输,库需要多次调用回调函数CopyRoutine,返回将要容纳已经收在硬件buf中数据的缓 冲区指针

IN过程,多个DATA_IN传输, 每次需要向主机传输数据时, USB库都会调用一次回调函数CopyRoutine,它返回一 个包含要发送的数据的缓冲区指针,库再把这个缓冲区的内容拷贝的硬件buf以择机发送出去

In0_Process(void)

完成描述符的输出准备:

DataStageln(void)

ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info;

ENDPOINT_INFO

pInformation

Out0_Process()

UserToMABufferCopy


百检网秉承“客户至上,服务为先,精诚合作,以人为本”的经营理念,始终站在用户的角度解决问题,为客户提供“一站购物式”的新奇检测体验,打开网站,像挑选商品一样简单,方便。打破行业信息壁垒,建构消费和检测机构之间高效的沟通平台