找回密码
 注册

微信扫码登录

使用验证码登录

只需一步,快速开始

胜天工科技销售各种数字电视信号调制卡胜天工科技销售各种数字电视信号码流卡

【游客、新手、注册会员的区别】 【积分策略和会员晋级说明】 【发帖和附件上传规则】 【如何下载感兴趣的资料】 【如何获取梦游币】 【侵权资料处理及免责说明】
查看: 2280|回复: 1

基于STM32的USB程序开发笔记(五)

[复制链接]
  • TA的每日心情
    开心
    2016-9-26 15:28
  • 签到天数: 56 天

    [LV.5]常住居民I

    发表于 2009-5-4 20:55:29 | 显示全部楼层 |阅读模式
    分享到:
    消息来自- 北京
    USB设备的枚举(下)

    ADS1118开发板
    基于TI 最新推出的面积最小的16位数模转换器件ADS1118,同时为我们展示了一个K型热电偶温度测量的简易低成本的方案。
    • 购买信息
    • 评测文章
    • 产品详情
    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设备的固件程序如有什么疑问,请朋友们多花几分种时间留言、讨论,共同学习与进步。
    踩过的脚印

    该用户从未签到

    发表于 2014-2-27 09:52:30 | 显示全部楼层
    消息来自- 美国
    好文!
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|数字电视开发网 ( 京ICP备16008897号-5 )

    GMT+8, 2026-5-14 08:34 , Processed in 0.125684 second(s), 24 queries , Gzip On.

    Powered by Discuz! X3.5

    © 2001-2026 Discuz! Team.

    快速回复 返回顶部 返回列表