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

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

USB事务处理:

在 USB 协议中,USB 的数据传输由信息包组成,这些信息包组合
起来可以构成完整的事务处理。USB 事务处理是 USB 主机和 USB 功能
设备之间数据传输的基本单位。USB 的信息包和事务处理具有特定的
格式。
USB二:深入解析STM32_USB-FS-Device_Lib 库_第1张图片

Packet buff的使用

每个双向EP对应两个packet buffer,分别
用于发送和接收软件通过packet buffer
• 软件通过packet buffer interface来访问它们
• 这些packet buffer的位置和大小都可配置,由buffer描述表指定
• Buffer描述表本身也在这块memory里,它自己的地址是由USB_BTABLE寄存器指定的
• USB外设硬件不会把本EP的数据溢出到与其相邻的其他packet
USB二:深入解析STM32_USB-FS-Device_Lib 库_第2张图片

usb_prop.c中:CustomHID_Reset(void)中

端点(EP)的初始化
首次连接主机,主机会下发从机复位
USB二:深入解析STM32_USB-FS-Device_Lib 库_第3张图片

USB二:深入解析STM32_USB-FS-Device_Lib 库_第4张图片

端点(EP)资源:

有8个双向端点
• 双向EP0是必备的
• 其他EPi,应用可同时使用收、发两个方向或只使用一个方向
USB二:深入解析STM32_USB-FS-Device_Lib 库_第5张图片

各device demo对EP的使用情况:

USB二:深入解析STM32_USB-FS-Device_Lib 库_第6张图片
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) 来指向它
USB二:深入解析STM32_USB-FS-Device_Lib 库_第7张图片

USB二:深入解析STM32_USB-FS-Device_Lib 库_第8张图片

初始化:
void USB_Init(void)

USB二:深入解析STM32_USB-FS-Device_Lib 库_第9张图片

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

USB二:深入解析STM32_USB-FS-Device_Lib 库_第10张图片

看一下初始化的过程:
pProperty->Init();
USB二:深入解析STM32_USB-FS-Device_Lib 库_第11张图片

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

USB二:深入解析STM32_USB-FS-Device_Lib 库_第12张图片
• 连接设备: PowerOn()
• 拉低PB14来使能USB_D+线上的上拉电阻
• 置位并复位FRES@USB_CNTR来对USB外设进行复位
• 复位ISTR所有位来清除可能pending的中断
• 设置本应用关心的中断事件: RESET、 SUSP、 WKUP

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

USB二:深入解析STM32_USB-FS-Device_Lib 库_第13张图片
不同应用关心不同的中断事件:

USB二:深入解析STM32_USB-FS-Device_Lib 库_第14张图片

USB二:深入解析STM32_USB-FS-Device_Lib 库_第15张图片
首先分析下RESET中断:
首次连接主机,主机会下发从机复位

1、 CustomHID_Reset

USB二:深入解析STM32_USB-FS-Device_Lib 库_第16张图片

USB二:深入解析STM32_USB-FS-Device_Lib 库_第17张图片

• 初始化EP

• EP0:控制类型;发送NAK、接收Valid;设置接收buffer地址和长度;设置发送buffer地址
• EP1:批量类型;发送NAK、接收Disable;设置发送buffer地址
• EP2:批量类型;发送Disable、接收VALID;设置接收buffer地址和长度

USB二:深入解析STM32_USB-FS-Device_Lib 库_第18张图片

USB二:深入解析STM32_USB-FS-Device_Lib 库_第19张图片

设置状态标志:

这里写图片描述

2、CTR中断

USB二:深入解析STM32_USB-FS-Device_Lib 库_第20张图片
• 硬件置位表示一个transaction正确完成了
• 软件需要根据EP_ID和DIR来得知这次transaction是发生在哪个EP上的何种
transaction(SETUP/OUT/IN transaction?)

USB二:深入解析STM32_USB-FS-Device_Lib 库_第21张图片

void CTR_LP(void)

USB二:深入解析STM32_USB-FS-Device_Lib 库_第22张图片

1、extract highest priority endpoint number

USB二:深入解析STM32_USB-FS-Device_Lib 库_第23张图片

(wIstr = _GetISTR());
EPindex = (u8)(wIstr & ISTR_EP_ID);

端口号为0时:
/* save RX & TX status */
/* and set both to NAK */

USB二:深入解析STM32_USB-FS-Device_Lib 库_第24张图片

参看传输方向

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()

USB二:深入解析STM32_USB-FS-Device_Lib 库_第25张图片

第一步:从EP0的接收Packet buf中读取主机命令
USB二:深入解析STM32_USB-FS-Device_Lib 库_第26张图片

pInformation->ControlState = SETTING_UP;

第二步:
USB二:深入解析STM32_USB-FS-Device_Lib 库_第27张图片

交互设备描述符:

USB二:深入解析STM32_USB-FS-Device_Lib 库_第28张图片

//函数指针参数为16位参数,返回值是指向8位变量的指针
u8 *(*CopyRoutine)(u16);

USB二:深入解析STM32_USB-FS-Device_Lib 库_第29张图片
【作用和意义】 通过这个函数获得用户的数据缓冲区地址,从而可以在OUT过程中把收到的数据拷贝到用户缓冲区,或在IN过程中把用户缓冲区的数据拷贝到USB发送缓冲区
OUT过程,多个DATA_OUT传输,库需要多次调用回调函数CopyRoutine,返回将要容纳已经收在硬件buf中数据的缓 冲区指针
IN过程,多个DATA_IN传输, 每次需要向主机传输数据时, USB库都会调用一次回调函数CopyRoutine,它返回一 个包含要发送的数据的缓冲区指针,库再把这个缓冲区的内容拷贝的硬件buf以择机发送出去

USB二:深入解析STM32_USB-FS-Device_Lib 库_第30张图片

USB二:深入解析STM32_USB-FS-Device_Lib 库_第31张图片

USB二:深入解析STM32_USB-FS-Device_Lib 库_第32张图片

In0_Process(void)

USB二:深入解析STM32_USB-FS-Device_Lib 库_第33张图片

完成描述符的输出准备:

DataStageln(void)
 ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info; 
ENDPOINT_INFO

USB二:深入解析STM32_USB-FS-Device_Lib 库_第34张图片

pInformation

USB二:深入解析STM32_USB-FS-Device_Lib 库_第35张图片

Out0_Process()

USB二:深入解析STM32_USB-FS-Device_Lib 库_第36张图片

UserToMABufferCopy

USB二:深入解析STM32_USB-FS-Device_Lib 库_第37张图片

USB二:深入解析STM32_USB-FS-Device_Lib 库_第38张图片

你可能感兴趣的:(USB)