你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32的USB程序开发笔记(五) ——USB设备的枚举(下)

[复制链接]
乡村布拉德 发布时间:2008-12-23 13:47
SETUP事件正确接收后,根据该事件提供的请求类型进行对主机的响应。SETUP数据结构的wLength字段说明的是请求返回或者提供的数据长度。
如果判断出的请求信息错误或者说不被支持,STM32 USB设备需要中断此次请求:
    SetEPR_RXStatus(ENDP0, EP_RX_VALID);
    SetEPR_TXStatus(ENDP0, EP_TX_STALL);

正确获取到请求信息后,如果wLength为0,设备需要发送一个0长度数据包以响应主机:
// *****************************************************************************
// Function Name  : SETUP0_Trans0Data
// Description    :
// Input          :
// Output         :
// Return         :
// *****************************************************************************
RESULT SETUP0_Trans0Data(void)
{
  // Send 0-length data frame as ACK to host
  SetBuffDescTable_TXCount(ENDP0, 0);
  SetEPR_RXStatus(ENDP0, EP_RX_NAK);
  SetEPR_TXStatus(ENDP0, EP_TX_VALID);

  return RESULT_SUCCESS;
}
如果wLength不为0,设备则需要根据请求的数据长度发送数据包以响应主机:
// *****************************************************************************
// Function Name  : SETUP0_TransData
// Description    :
// Input          :
// Output         :
// Return         :
// *****************************************************************************
RESULT SETUP0_TransData(void)
{
  unsigned short wLength = vsDeviceInfo.TransInfo.wLength;
  unsigned short wOffset = vsDeviceInfo.TransInfo.wOffset;
  unsigned short wMaxSize = vsDeviceInfo.TransInfo.wPacketSize;

  if(wLength)
  {
    if(wLength > wMaxSize)
    {
      wLength = wMaxSize;     
    }
 
    // Copy the transfer buffer to the endpoint0's buffer
    BufferCopy_UserToPMA( vsDeviceInfo.TransInfo.pBuffer+wOffset,   // transfer buffer
                          GetBuffDescTable_TXAddr(ENDP0),           // endpoint 0 TX address
                          wLength);

    SetBuffDescTable_TXCount(ENDP0, wLength);
    SetEPR_RXStatus(ENDP0, EP_RX_NAK);
    SetEPR_TXStatus(ENDP0, EP_TX_VALID);
 
    // Update the data lengths
    vsDeviceInfo.TransInfo.wLength -= wLength;
    vsDeviceInfo.TransInfo.wOffset += wLength;
   
    return RESULT_LASTDATA;        
  }

  return RESULT_SUCCESS;
}
如果发送的数据长度大于端点设置的最大数据包长度,数据将分割为若干次发送,记录发送数据的状态包含在结构体TRANSFER_INFO中:
// *****************************************************************************
// TRANSFER_INFO
// *****************************************************************************
typedef struct _TRANSFER_INFO
{
  unsigned short wLength;                 // total lengths data will be transmit
  unsigned short wOffset;                 // number of data be transmited
  unsigned short wPacketSize;             // endpoints packet max size
  unsigned char* pBuffer;                 // address of data buffer
}
TRANSFER_INFO,
*PTRANSFER_INFO;
TRANSFER_INFO.wLength记录发送的数据长度,如果非0,表示有数据需要被发送。
TRANSFER_INFO.wOffset记录已发送的数据长度,用以确定数据缓冲TRANSFER_INFO.pBuffer的偏移量。

需要了解的一点:USB主机向USB设备正确发送一请求后(这部分的处理由硬件完成),USB主机将间隔若干次的向USB设备索取响应数据,STM32 USB TX状态为NAK说明不响应USB主机,USB主机在超时后退出此次请求;TX状态为STLL说明中断此次请求,USB主机将无条件退出请求;TX状态为 VALID说明设备已准备好数据发送,USB主机将从USB设备读取数据。
以非0长度数据请求的GET_DESCRIPTOR请求为例的响应过程:
CTR_SETUP0()->SETUP0_Data()->SR_GetDescriptor()->SETUP0_TransData()

RESULT SR_GetDescriptor(void)
{
  // RequestType: device->host, standard request and device recipient
  if(vsDeviceInfo.SetupData.bmRequestType == RT_D2H_STANDARD_DEVICE)
  {
    // SetupData.wValue.b.MSB: descriptor type
    // SetupData.wValue.b.LSB: descriptor index
    switch(vsDeviceInfo.SetupData.wValue.b.MSB)
    {
    case DESCRIPTOR_DEVICE:               return SR_GetDescriptor_Device();
    case DESCRIPTOR_CONFIG:               return SR_GetDescriptor_Config();
    case DESCRIPTOR_STRING:               return SR_GetDescriptor_String();

    default:  return RESULT_UNSUPPORT;
    }
  }

  return RESULT_UNSUPPORT;
}
GET_DESCRIPTOR请求属于USB协议中的标准请求(standard request)并且数据方向为设备至主机(device->host),分设备描述符、配置描述符、字符串描述符三种。已设备描述符为例:
RESULT SR_GetDescriptor_Device(void)
{
  // Assigned the device descriptor to the transfer
  vsDeviceInfo.TransInfo.wOffset = 0;
  vsDeviceInfo.TransInfo.wPacketSize = ENDP0_PACKETSIZE;
  vsDeviceInfo.TransInfo.pBuffer = DescBuffer_Device.pBuff;
  vsDeviceInfo.TransInfo.wLength = DescBuffer_Device.wLen;
  vsDeviceInfo.eControlState = CS_GET_DESCRIPTOR;

  if(vsDeviceInfo.TransInfo.wLength > vsDeviceInfo.SetupData.wLength.w)
  {
    vsDeviceInfo.TransInfo.wLength = vsDeviceInfo.SetupData.wLength.w;
  }

  return SETUP0_TransData();
}
这里说明了发送数据的长度、缓冲、偏移、端点包大小以及当前的控制状态,并说明了如果发送的数据长度超出请求的数据长度,则将舍弃超出的部分。数据配置好后,调用SETUP0_TransData()进行数据发送。
在USB主机查询到USB设备准备就绪后,将读取出这些数据,完成后,USB设备将产生IN事件,此时将响应CTR_IN0()函数:
// *****************************************************************************
// Function Name  : CTR_IN
// Description    :
// Input          :
// Output         :
// Return         :
// *****************************************************************************
void CTR_IN0(void)
{
  switch(vsDeviceInfo.eControlState)
  {
  case CS_GET_DESCRIPTOR:
    if(SETUP0_TransData() == RESULT_SUCCESS)
    {
      SetEPR_TXStatus(ENDP0, EP_TX_NAK);
      SetEPR_RXStatus(ENDP0, EP_RX_VALID);
    }   
    break;
     
  case CS_SET_ADDRESS:
    SetEPR_TXStatus(ENDP0, EP_TX_NAK);
    SetEPR_RXStatus(ENDP0, EP_RX_VALID);

    SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress);
    vsDeviceInfo.eDeviceState = DS_ADDRESSED;   
    break;

  case CS_SET_CONFIGURATION:
    SetEPR_TXStatus(ENDP0, EP_TX_NAK);
    SetEPR_RXStatus(ENDP0, EP_RX_VALID);

    vsDeviceInfo.eDeviceState = DS_CONFIGURED;
    break;
 
  default:
    break;
  }
}
再这如果响应GET_DESCRIPTOR请求发送的数据如果全部发送完毕,SETUP0_TransData()返回RESULT_SUCCESS,并设置TX状态为NAK;否则返回RESULT_LASTDATA,将继续发送剩余的数据直到数据全部被发送。至此,整个的GET_DESCRIPTOR请求过程完成。
0长度的数据请求在发送0长度数据响应后,因为不存在可能还未传送的数据,因而IN事件后直接结束此次请求。
在数据方向为USB主机->USB设备时,如果正确接收到数据,将响应CTR_OUT0()函数,处理过程类同CTR_IN0()函数。
在USB设备的枚举过程中,USB的一些描述符数据结构需要了解,具体在USB协议中有详细的说明,在usb_desc(.c/.h)文件中,定义了这些结构,这些结构是特定的:

设备描述符:长度、格式固定,其中VENDOR_ID与PRODUCT_ID决定上位机驱动的识别。设备分属类别决定了设备的性质,如果为自定义USB设备,设备分属类别值为0,同时上位机驱动必须配合编写;如果为标准USB设备,则必须使用这些标准设备的驱动、数据结构等等,条件是你必须了解这些标准设备的一些信息,好处是省去一些麻烦的驱动编写。
const unsigned char cbDescriptor_Device[DESC_SIZE_DEVICE] =
{
  DESC_SIZE_DEVICE,     // bLength: 18
  DESCRIPTOR_DEVICE,    // descriptor type

  0x00,            // bcdUSB LSB: USB release number -> USB2.0
  0x02,       // bcdUSB MSB: USB release number -> USB2.0

  0x00,         // bDeviceClass:    Class information in the interface descriptors
  0x00,       // bDeviceSubClass:
  0x00,       // bDeviceProtocol:
  0x40,       // bMaxPacketSize0:  LowS(8), FullS(8,16,32,64), HighS(64)

  LOWORD(VENDOR_ID),       // idVendor LSB:
  HIWORD(VENDOR_ID),       // idVendor MSB:

  LOWORD(PRODUCT_ID),      // idProduct LSB:
  HIWORD(PRODUCT_ID),      // idProduct MSB:

  LOWORD(DEVICE_VERSION),  // bcdDevice LSB:
  HIWORD(DEVICE_VERSION),  // bcdDevice MSB:

  0x01,       // iManufacturer: Index of string descriptor describing manufacturer
  0x02,       // iProduct: Index of string descriptor describing product
  0x03,       // iSerialNumber: Index of string descriptor describing the device serial number

  0x01        // bNumConfigurations: number of configurations
};

配置描述符:前9个字节格式固定,后面紧跟的各种描述结构跟实际配置有关,每增加一种描述结构,该描述结构的第一字节说明了结构的长度,第二直接说明了结构的类型。在配置描述符中一般包含配置描述、接口描述、端点描述,如果需要同样可增加自定义的描述。使用标准USB设备类别时,配置描述符的结构也必须满足此类标准设备的数据结构。
const unsigned char cbDescriptor_Config[DESC_SIZE_CONFIG] =
{
  // Descriptor of configuration
  0x09,                   // lengths   
  DESCRIPTOR_CONFIG,      // descriptor type

  DESC_SIZE_CONFIG,       // Total configuration descriptor lengths LSB
  0x00,                   // Total configuration descriptor lengths MSB

  0x01,       // bNumInterfaces: Total number of interfaces
  0x01,       // bConfigurationValue: Configuration value
  0x00,       // iConfiguration: Index of string descriptor describing the configuration
 
  0xA0,       // bmAttributes: bus powered
                        // bit 4...0 : Reserved, set to 0
                        // bit 5     : Remote wakeup (1:yes)
                        // bit 6     : Self power (1:yes)
                        // bit 7     : Reserved, set to 1

  0x32,       // bMaxPower: this current is used for detecting Vbus = 100mA


  // Descriptor of interface
  0x09,
  DESCRIPTOR_INTERFACE,

  0x00,       // bInterfaceNumber: Number of Interface
  0x00,       // bAlternateSetting: Alternate setting

  0x02,       // bNumEndpoints: Number of endpoints except EP0
  0x00,       // bInterfaceClass:
  0x00,       // bInterfaceSubClass:
  0x00,       // nInterfaceProtocol:

  0x00,       // iInterface: Index of string descriptor describing the interface


  // Descriptor of endpoint1 OUT
  0x07,
  DESCRIPTOR_ENDPOINT,

  0x01,       // bEndpointAddress
              // bit 3...0 : the endpoint number
              // bit 6...4 : reserved
              // bit 7     : 0(OUT), 1(IN)

  0x03,       // bmAttributes
              // bit 1...0 : Transfer type
              //                00(CONTROL), 01(ISOCHRONOUS), 10(BULK), 11(INTERRUPT)
              // bit 3...2 : Synchronization type
              //                00(No Synch), 01(Asynchronous), 10(Adaptive), 11(Synchronous)
              // bit 5...4 : Endpoint Usage type
              //                00(data), 01(Feedback), 10(Implicit feedback data endpoint), 11(Reserved)
              // bit 7...6 : Reserved, must be zero

  0x40,       // packet size LSB
  0x00,       // packet size MSB

  0x20,        // polling interval time: 32ms

  // Descriptor of endpoint2 IN
  0x07,
  DESCRIPTOR_ENDPOINT,

  0x82,       // bEndpointAddress
              // bit 3...0 : the endpoint number
              // bit 6...4 : reserved
              // bit 7     : 0(OUT), 1(IN)

  0x03,       // bmAttributes
              // bit 1...0 : Transfer type
              //                00(CONTROL), 01(ISOCHRONOUS), 10(BULK), 11(INTERRUPT)
              // bit 3...2 : Synchronization type
              //                00(No Synch), 01(Asynchronous), 10(Adaptive), 11(Synchronous)
              // bit 5...4 : Endpoint Usage type
              //                00(data), 01(Feedback), 10(Implicit feedback data endpoint), 11(Reserved)
              // bit 7...6 : Reserved, must be zero

  0x40,       // packet size LSB
  0x00,       // packet size MSB

  0x20        // polling interval time: 32ms
};

字符串描述符:定义了与设备有关的一些信息,常见的为以下四种,如果有需要,同样可以定义自己的字符串描述符。
const unsigned char cbDescriptor_StringLangID[DESC_SIZE_STRING_LANGID] =
{
  DESC_SIZE_STRING_LANGID,  // bLength
  DESCRIPTOR_STRING,        // bDescriptorType = String Descriptor
   
  0x09,                                  // LangID LSB:
  0x04                                  // LangID MSB: 0x0409(U.S. English)
};

const unsigned char cbDescriptor_StringVendor[DESC_SIZE_STRING_VENDOR] =
{
  DESC_SIZE_STRING_VENDOR,  // bLength
  DESCRIPTOR_STRING,        // bDescriptorType = String Descriptor

  // String: "LaBiXiaoXiaoXin"
  'L',0, 'a',0, 'B',0, 'i',0, 'X',0, 'i',0, 'a',0, 'o',0,
   'X',0, 'i',0, 'a',0, 'o',0, 'X',0, 'i',0, 'n',0
};

const unsigned char cbDescriptor_StringProduct[DESC_SIZE_STRING_PRODUCT] =
{
  DESC_SIZE_STRING_PRODUCT, // bLength
  DESCRIPTOR_STRING,        // bDescriptorType = String Descriptor

    // String: "STM32 ezUSB-CORE V1.01"
  'S',0, 'T',0, 'M',0, '3',0, '2',0, ' ',0, 'e',0, 'z',0, 'U',0, 'S',0, 'B',0,
  '-',0, 'C',0, 'O',0, 'R',0, 'E',0, ' ',0, 'V',0, '1',0, '.',0, '0',0, '1',0
};

const unsigned char cbDescriptor_StringSerial[DESC_SIZE_STRING_SERIAL] =
{
  DESC_SIZE_STRING_SERIAL,  // bLength
  DESCRIPTOR_STRING,        // bDescriptorType = String Descriptor

    // String: "ezUSB-CORE Demo 2008/11/18"
  'e',0, 'z',0, 'U',0, 'S',0, 'B',0, '-',0, 'C',0, 'O',0, 'R',0, 'E',0, ' ',0,
  'D',0, 'e',0, 'm',0, 'o',0, ' ',0, '2',0, '0',0, '0',0, '8',0, '/',0, '1',0, '1',0, '/',0, '1',0, '8',0
};

了解这些描述符的用法以及作用,最好的方法的是编写自定义的USB上位机驱动以及应用程序,这样你可以深刻了解USB设备与主机间的数据交换方式以及实现手段,下篇将开始介绍USB上位机驱动以及应用程序的编写以及开发环境的建立,STM32 USB设备的固件程序如有什么疑问,请朋友们多花几分种时间留言、讨论,共同学习与进步。
收藏 评论3 发布时间:2008-12-23 13:47

举报

3个回答
圣手008 回答时间:2012-10-30 20:21:23

回复:基于STM32的USB程序开发笔记(五) ——USB设备的枚举(下)

找不到设备啊
lin1620c 回答时间:2012-11-12 23:51:57

RE:基于STM32的USB程序开发笔记(五) ——USB设备的枚举(下)

谢谢!!!!!非常谢谢
123-370895 回答时间:2013-12-18 11:34:50

RE:基于STM32的USB程序开发笔记(五) ——USB设备的枚举(下)

强烈支持

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版