• stm32f4 USB项目开发详解


    一、USB总体概况

     

    1.1、USB通信过程简介

    1. 设备插到主机上
    2. 主机开始检测设备类型(高速/全速/低速)
    3. 复位设备
    4. 主机开始对设备枚举(根据枚举得到的各种信息加载合适的驱动程序,比如根据信息知道是一个鼠标设备,则加载鼠标的驱动程序对接下来的数据进行处理)
    5. 枚举完成后主机要发送令牌包(IN / OUT)查询有效端点是否有数据,有数据时设备自然会返还给主机

     

    1.2、USB枚举过程简介

    1. 主机获取设备描述符(部分)
    2. 主机对从机设置设备地址(非零,相当于我们的学号id)
    3. 主机再次获取从机设备描述符(全部)
    4. 主机获取配置描述符(了解从机配置,接口,端点)情况
    5. 如果有字符串描述符还有获取字符串描述符
    6. 设置配置请求,就是激活配置,如果没有这一步对应的配置就不可用
    7. 针对不同的类,获取它们独特的类描述符(比如HID报告描述符)

    注意:

    1. 上面的枚举1-4,6步骤是必须的,
    2. 主机和从机通信时,从机时不能主动发数据给主机的,必须要等主机给从机发送令牌包后,根据主机的需求发送相应的数据

    1.3、USB 配置  接口  端点  的关系

      1、一个设备可以有多个配置,不同的配置对应不同的功能

        比如,一个USB接口CDROM,作为一个设备,

        它具有两种功能,1读取光盘 和  2播CD,所以有2Configuration描述符

      2、一个功能的实现要涉及许多接口,

        比如CD播放机使用时,需要音频接口,同时还需要控制CD机的接口。 

      3、一个接口又有许多端点组成,一般真正通信都是针对端点进行的,比如用端点0来进行控制枚举传输

        stm32  支持8个双向端点,16个单向端点,每个端点只能时一个方向(OUT / IN),除了端点0

     、

    二、区分高速全速 低速设备的方法

    d+上面接电阻:全速/高速设备             d-  上接电阻:低速设备

     、

    三、USB设备插拔检测机制

    3.1、没有插上usb设备主机情况

    1. D+D-数据线上的下拉电阻起作用,使得二者都在低电平;主机端看来就是个SE0状态;
    2. 同样地,当数据线上的SE0状态持续一段时了,就被主机认为是断开状态 

    3.2、 插上usb设备时

    当主机检测到某一个数据线电平拉高并保持了一段时间,就认为有设备连上来了

    主机必需在驱动SE0状态以复位设备之前, 立刻采样总线状态来判断设备的速度 

     四、USB通信数据包解释以及包的传输过程

     4.1、Packet的组成

    4.2 、USB包的分类(令牌,数据包,握手包、帧首包)

    包的分类组要是靠PID域来说明的,其中令牌包最重要

     

    4.2.1、令牌包:

    IN  OUT  SETUP  三个命令可选,并且都由主机发出,用来启动一次传输

    主机和从机通信时,从机时不能主动发数据给主机的,必须要等主机给从机发送令牌包后,根据主机的需求发送相应的数据

      1、IN   主机用来从设备读取数据 

      2、OUT   主机用来向设备发送数据

      3、SETUP  主机用来向设备发送控制命令,接下来就是主机发送一次命令数据给(这个数据一般时获取描述符请求)从机  一般枚举的时候用

    例如SETUP包:

     4.2.2、数据包:

    数据包:一般是DATA0 DATA1交替发出,如本次发送DATA0 下次就发送DATA1,主要目的就是为了校验

    图中DATA0 编码是0xC3

     

     4.2.3、握手包

    用来确认应答

      >> ACK:传输正确完成
      >> NAK:设备暂时没有准备好接收数据,或没有准备好发送数据
      >> STALL:设备不能进行传输
      >> NYET/ERR:仅用于高速传输,设备没有准备好或出错

    4.3、包的传输顺序

    一般都是先由主机发送命令,然后才是数据过程,再时应答过程

    五、枚举过程

    枚举过程都是主机发送标准请求然后从机做出相应的应答

    5.1、枚举过程的通俗比喻 

    1、主机(下称H):你是什么设备(获取设备描述符请求)?
      设备(下称D):12 01 0100……Device Descriptor
    2、H:你有几种功能(获取配置描述符请求)?
      D:09 02 09……Configuration Descriptor
    3、H:每个功能有几个接口(获取接口描述符)?
      D:09 04 00……Interface Descriptor
    4、H:每个接口使用哪几个端点(获取端点描述符)?
      D:06 05 82……Endpoint Descriptor
    5、H:好了,我知道你是谁了,开始传输数据吧!
      D:OK, Read Go

    5.2、主机标准请求

    主机要获得从机的描述符就要发送相关8字节标准请求

     

     5.3、描述符介绍

    5.3.1、设备描述符

     以stm32为例:

    /*usb_desc.c*/
    
    /* USB Standard Device Descriptor */
    const uint8_t Joystick_DeviceDescriptor[JOYSTICK_SIZ_DEVICE_DESC] =
      {
        0x12,                       /*bLength */
        USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/
        0x00,                       /*bcdUSB */
        0x02,
        0x00,                       /*bDeviceClass*/
        0x00,                       /*bDeviceSubClass*/
        0x00,                       /*bDeviceProtocol*/
        0x40,                       /*bMaxPacketSize 64*/
        0x83,                       /*idVendor (0x0483)*/
        0x04,
        0x10,                       /*idProduct = 0x5710*/
        0x57,
        0x00,                       /*bcdDevice rel. 2.00*/
        0x02,
        1,                          /*Index of string descriptor describing
                                                      manufacturer */
        2,                          /*Index of string descriptor describing
                                                     product*/
        3,                          /*Index of string descriptor describing the
                                                     device serial number */
        0x01                        /*bNumConfigurations*/
      }
      ; /* Joystick_DeviceDescriptor */

    5.3.2、配置描述符

     

        0x09, /* bLength: Configuration Descriptor size */
        USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
        JOYSTICK_SIZ_CONFIG_DESC,  /*#define JOYSTICK_SIZ_CONFIG_DESC   34   这是一个大数组,整个数组大小34字节*/
        /* wTotalLength: Bytes returned   */
        0x00,
        0x01,         /*bNumInterfaces: 1 interface*/
        0x01,         /*bConfigurationValue: Configuration value*/
        0x00,         /*iConfiguration: Index of string descriptor describing
                                         the configuration*/
        0xE0,         /*bmAttributes: Self powered */
        0x32,         /*MaxPower 100 mA: this current is used for detecting Vbus*/

    5.3.3、 接口描述符

     

        /************** Descriptor of Joystick Mouse interface ****************/
        /* 09 */
        0x09,         /*bLength: Interface Descriptor size*/
        USB_INTERFACE_DESCRIPTOR_TYPE,/*bDescriptorType: Interface descriptor type*/
        0x00,         /*bInterfaceNumber: Number of Interface 序号,编号从0开始,第二个接口就是1*/
        0x00,         /*bAlternateSetting: Alternate setting*/
        0x01,         /*bNumEndpoints  该接口的端点数目*/
        0x03,         /*bInterfaceClass: HID*/
        0x01,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
        0x02,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
        0,            /*iInterface: Index of string descriptor*/

     5.3.4、类描述符(比如HID类描述符)这个时可选的(如果不是标准类,就不需要)

     

       /******************** Descriptor of Joystick Mouse HID ********************/
        /* 18 */
        0x09,         /*bLength: HID Descriptor size*/
        HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
        0x00,         /*bcdHID: HID Class Spec release number*/
        0x01,
        0x00,         /*bCountryCode: Hardware target country*/
        0x01,         /*bNumDescriptors: Number of HID class descriptors to follow*/
        0x22,         /*bDescriptorType*/
        JOYSTICK_SIZ_REPORT_DESC,/*wItemLength: Total length of Report descriptor*/
        0x00,
        /******************** Descriptor of Joystick Mouse endpoint ********************/

     5.3.5、端点描述符

        /******************** Descriptor of Joystick Mouse endpoint ********************/
        /* 27 */
        0x07,          /*bLength: Endpoint Descriptor size*/
        USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    
        0x81,          /*bEndpointAddress: Endpoint Address (IN)*/
        0x03,          /*bmAttributes: Interrupt endpoint 0控制  1等时  2批量  3中断*/
        0x04,          /*wMaxPacketSize: 4 Byte max */
        0x00,
        0x20,          /*bInterval: Polling Interval (32 ms)*/

    5.4、获取设备描述符

     

     

    5.5、设置地址请求

     

     5.6、获取配置描述符

     5.7、设置配置请求

    就是让某个配置有效

     

    5.8、stm32 枚举代码分析

    /*usbh_core.c*/

    void
    USBH_Process(USB_OTG_CORE_HANDLE *pdev , USBH_HOST *phost) { volatile USBH_Status status = USBH_FAIL; switch (phost->gState) { 、、、、、、、、、、、、 case HOST_ENUMERATION: /* Check for enumeration status */ if ( USBH_HandleEnum(pdev , phost) == USBH_OK) { /* The function shall return USBH_OK when full enumeration is complete */ /* user callback for end of device basic enumeration */ phost->usr_cb->EnumerationDone(); phost->gState = HOST_USR_INPUT; } break; 、、、、、、、、、、 default : break; } }
    /*usbh_core.c*/

    /*
    * * @brief USBH_HandleEnum * This function includes the complete enumeration process * @param pdev: Selected device * @retval USBH_Status */ static USBH_Status USBH_HandleEnum(USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost) { USBH_Status Status = USBH_BUSY; uint8_t Local_Buffer[64]; switch (phost->EnumState) { case ENUM_IDLE: /* Get Device Desc for only 1st 8 bytes : To get EP0 MaxPacketSize 得到8字节设备描述符 */ if ( USBH_Get_DevDesc(pdev , phost, 8) == USBH_OK) { phost->Control.ep0size = phost->device_prop.Dev_Desc.bMaxPacketSize; /* Issue Reset 复位设备 */ HCD_ResetPort(pdev); phost->EnumState = ENUM_GET_FULL_DEV_DESC; /* modify control channels configuration for MaxPacket size */ USBH_Modify_Channel (pdev, phost->Control.hc_num_out, 0, 0, 0, phost->Control.ep0size); USBH_Modify_Channel (pdev, phost->Control.hc_num_in, 0, 0, 0, phost->Control.ep0size); } break; case ENUM_GET_FULL_DEV_DESC: /* Get FULL Device Desc 得到整个设备描述符 */ if ( USBH_Get_DevDesc(pdev, phost, USB_DEVICE_DESC_SIZE) == USBH_OK) { /* user callback for device descriptor available */ phost->usr_cb->DeviceDescAvailable(&phost->device_prop.Dev_Desc); phost->EnumState = ENUM_SET_ADDR; } break; case ENUM_SET_ADDR: /* 设置地址 set address */ if ( USBH_SetAddress(pdev, phost, USBH_DEVICE_ADDRESS) == USBH_OK) /*#define USBH_DEVICE_ADDRESS 1*/ { USB_OTG_BSP_mDelay(2); phost->device_prop.address = USBH_DEVICE_ADDRESS; /* user callback for device address assigned */ phost->usr_cb->DeviceAddressAssigned(); phost->EnumState = ENUM_GET_CFG_DESC; /* modify control channels to update device address */ USBH_Modify_Channel (pdev, phost->Control.hc_num_in, phost->device_prop.address, 0, 0, 0); USBH_Modify_Channel (pdev, phost->Control.hc_num_out, phost->device_prop.address, 0, 0, 0); } break; case ENUM_GET_CFG_DESC: /* 得到配置描述符get standard configuration descriptor */ if ( USBH_Get_CfgDesc(pdev, phost, USB_CONFIGURATION_DESC_SIZE) == USBH_OK) { phost->EnumState = ENUM_GET_FULL_CFG_DESC; } break; case ENUM_GET_FULL_CFG_DESC: /* get FULL config descriptor (config, interface, endpoints) */ if (USBH_Get_CfgDesc(pdev, phost, phost->device_prop.Cfg_Desc.wTotalLength) == USBH_OK) { /* User callback for configuration descriptors available */ phost->usr_cb->ConfigurationDescAvailable(&phost->device_prop.Cfg_Desc, phost->device_prop.Itf_Desc, phost->device_prop.Ep_Desc[0]); phost->EnumState = ENUM_GET_MFC_STRING_DESC; } break; case ENUM_GET_MFC_STRING_DESC: if (phost->device_prop.Dev_Desc.iManufacturer != 0) { /* 字符串描述符Check that Manufacturer String is available */ if ( USBH_Get_StringDesc(pdev, phost, phost->device_prop.Dev_Desc.iManufacturer, Local_Buffer , 0xff) == USBH_OK) { /* User callback for Manufacturing string */ phost->usr_cb->ManufacturerString(Local_Buffer); phost->EnumState = ENUM_GET_PRODUCT_STRING_DESC; } } else { phost->usr_cb->ManufacturerString("N/A"); phost->EnumState = ENUM_GET_PRODUCT_STRING_DESC; } break; case ENUM_GET_PRODUCT_STRING_DESC: if (phost->device_prop.Dev_Desc.iProduct != 0) { /* Check that Product string is available */ if ( USBH_Get_StringDesc(pdev, phost, phost->device_prop.Dev_Desc.iProduct, Local_Buffer, 0xff) == USBH_OK) { /* User callback for Product string */ phost->usr_cb->ProductString(Local_Buffer); phost->EnumState = ENUM_GET_SERIALNUM_STRING_DESC; } } else { phost->usr_cb->ProductString("N/A"); phost->EnumState = ENUM_GET_SERIALNUM_STRING_DESC; } break; case ENUM_GET_SERIALNUM_STRING_DESC: if (phost->device_prop.Dev_Desc.iSerialNumber != 0) { /* Check that Serial number string is available */ if ( USBH_Get_StringDesc(pdev, phost, phost->device_prop.Dev_Desc.iSerialNumber, Local_Buffer, 0xff) == USBH_OK) { /* User callback for Serial number string */ phost->usr_cb->SerialNumString(Local_Buffer); phost->EnumState = ENUM_SET_CONFIGURATION; } } else { phost->usr_cb->SerialNumString("N/A"); phost->EnumState = ENUM_SET_CONFIGURATION; } break; case ENUM_SET_CONFIGURATION: /* 设置配置请求set configuration (default config) */ if (USBH_SetCfg(pdev, phost, phost->device_prop.Cfg_Desc.bConfigurationValue) == USBH_OK) { phost->EnumState = ENUM_DEV_CONFIGURED; } break; case ENUM_DEV_CONFIGURED: /* user callback for enumeration done */ Status = USBH_OK; break; default: break; } return Status; }

     六、 USB枚举后的数据传输过程  以及    四种传输类型

    usb 四种传输类型是针对端点而言的,并不是usb整个传输过程为一种传输类型

    1、控制传输(Control Transfers):  枚举  

      (一般用于枚举过程端点0,主机给从机发命令或回应状态时,这个过程是随机突发的的,也就是主机随时发命令,从机只能是待命)

     

    2、大容量数据传输 批量传输(Bulk Transfers):       U盘

            大容量传输一般用于非零端点,并且针对大数据传输(如U盘),数据可以占用任意带宽,并容忍延迟 ,并且这个过程也是随机突发的,因为控制权在主机,用户随时要读取设备(u盘)数据,所以这个过程是非周期的

     

    stm32 大容量传输分析

    3、同步传输(Isochronous Transfers):    摄像头           

            周期性的,持续性的传输,用于传输与时效相关的信息,并且在数据中保存时间戳的信息 比如(摄像头图像传

     

    4、中断传输(Interrupt Transfers):   鼠标键盘

      周期性,低频率,比如hid鼠标键盘主机是周期性查询端点有没有数据的,只要鼠标有动作,就可以往端点缓存送数据,等到主机查询的时候,数据就会被主机读走)所以在设备初始化的时候要初始化主机查询时间间隔

    超时轮询时间间隔在从机端点描述符中获取

     stm32中断传输分析

    /*主机 usbh_hid_core.c*/
    
    static USBH_Status USBH_HID_Handle(USB_OTG_CORE_HANDLE *pdev , 
                                       void   *phost)
    {
     、、、、、、switch (HID_Machine.state)
      {
        
     、、、、、case HID_GET_DATA:
    
        USBH_InterruptReceiveData(pdev, 
                                  HID_Machine.buff,
                                  HID_Machine.length,
                                  HID_Machine.hc_num_in);
        start_toggle = 1;
        
        HID_Machine.state = HID_POLL;
        HID_Machine.timer = HCD_GetCurrentFrame(pdev);
        break;
        
      case HID_POLL: 
        if(( HCD_GetCurrentFrame(pdev) - HID_Machine.timer) >= HID_Machine.poll)//超时轮询
        {
          HID_Machine.state = HID_GET_DATA;
        }
        else if(HCD_GetURB_State(pdev , HID_Machine.hc_num_in) == URB_DONE)
        {
          if(start_toggle == 1) /* handle data once */
          {
            start_toggle = 0;
            HID_Machine.cb->Decode(HID_Machine.buff);
          }
        }
        else if(HCD_GetURB_State(pdev, HID_Machine.hc_num_in) == URB_STALL) /* IN Endpoint Stalled */
        {
          
          /* Issue Clear Feature on interrupt IN endpoint */ 
          if( (USBH_ClrFeature(pdev, 
                               pphost,
                               HID_Machine.ep_addr,
                               HID_Machine.hc_num_in)) == USBH_OK)
          {
            /* Change state to issue next IN token */
            HID_Machine.state = HID_GET_DATA;
            
          }
          
        }      
        break;
        
      default:
        break;
      }
      return status;
    }
  • 相关阅读:
    Asp.net MVC 3实例学习之ExtShop(三)——完成首页
    Asp.net MVC 3实例学习之ExtShop(一)————创建应用并设置开发环境
    JDK里的设计模式
    Linux系统下ssh的相关配置
    简单介绍asp模式与saas模式
    Linux系统中xorg.conf文件简介
    [C++] MurmurHash2的性能
    [C++] 在程序里调用DOS命令
    Android SDK 1.5中文版 (Application基础—1)
    linux系统单网卡绑定双IP的方法
  • 原文地址:https://www.cnblogs.com/shenLong1356/p/11287833.html
Copyright © 2020-2023  润新知