精华内容
下载资源
问答
  • 来源:公众号【鱼鹰谈单片机】作者:鱼鹰OspreyID :emOsprey上篇笔记,大家了解了一些枚举相关的概念,本篇笔记将详述 CDC 设备枚举过程,让大家对整体的枚举过程有个概念...

    来源:公众号【鱼鹰谈单片机】

    作者:鱼鹰Osprey

    ID   :emOsprey

    上篇笔记,大家了解了一些枚举相关的概念,本篇笔记将详述 CDC 设备枚举过程,让大家对整体的枚举过程有个概念。

    为了更好到理解并分析接下来的通信流程,鱼鹰首先介绍标准请求描述符

    上篇笔记了解了标准请求和描述符是怎么回事,但还不够,还需要更细节的东西。

    首先从标准请求的8字说起:

    第一字节:位图请求信息。

    D7 代表了接下来传输的数据是从主机到从机,还是从机到主机的。我们知道枚举过程使用控制传输,有三个阶段,如果有数据阶段,那么这个阶段的DATA数据是由主机发出还是由从机发出,就看这个位的值了。

    当然了,因为每一次事务都有令牌包存在,所以IN令牌后的数据一定是由从机发出的,但是标准请求的这个位可以让从机做好发送数据或者接收数据的准备。

    D6~D5:代表了请求类型。

    请求类型代表本次请求属于什么类型的请求。目前有三类,标准、类、厂商。标准请求主要有:

    除了标准请求,还有类请求,比如 CDC 类,用到了三种类请求:

    SET_LINE_CODING(0x20)          设置串口波特率、起始位、停止位、流控等信息

    GET_LINE_CODING(0x21)        获取串口波特率、起始位、停止位、流控等信息

    SET_CONTROL_LINE_STATE(0x22)用于设置串口的状态

    厂商请求一般不会用于标准设备,CDC 类就没有用到(如果需要的话,应该也是能发出的)。

    D4~D0:代表了请求类型。

    因为请求的内容可能是面向设备,也可能面向接口、端点,所以这个域确定了本次请求面向的对象,这样设备可以根据请求的对象作相应的措施。

    字节:bRequest请求代码,即上面的几种请求代码,每个请求都会有请求代码,代表了具体请求。

    三四字节:wValue这个双字节主要根据bReuest来确定含义,比如如果是获取描述符(GET_DESCRIPTOR),而描述符有很多种,比如设备描述符、字符串描述符、配置描述符,那么到底主机要获取什么描述符?就看这个双字的高字节了。如果字节为 1,代表获取设备描述符,高字节为 2,代表获取配置描述符。

    总之这个值的具体含义需要根据请求代码来确定,而每一种请求代码都会明确规定wValue具体含义。

    五六字节:wIndex这个值和上面一样,也是需要请求代码来确定含义的。比如在获取产品序列号字符串时,这个值代表了语言 ID,告诉从机需要返回什么哪种字符串,当值为0x0409时代表英语。

    七八字节:wLenth这个值代表接下来主机会发送或者需要接收字节长度。

    一般来说,主机会根据需要在接下来的数据阶段获取或发送指定长度数据,主机发送的数据因为是由主机控制的,所以可以很容易确定这个值,但是因为主机并不清楚从机到底有多少数据会返回,所以这个值可能会比实际的更大。

    比如第一次获取设备描述符时,因为主机不清楚这个描述符多长,一般会比实际的描述符长度更大,所以如果从机没有足够的数据返回,那么只要返回从机能返回的最大数据即可;

    但是如果主机请求返回的数据比从机实际的数据,那么从机就按照主机的要求来就行,不必把自己所有的数据返回。

    以上就是标准请求的内容。设备返回的描述符通用格式比较简单:

    第一字节:描述符总长度(包括本字节)

    第二字节:描述符类型(对应标准请求wValue的高字节

    其余字节就代表了这个描述符的具体内容了,每种描述的具体内格式都不相同,需要根据实际的描述符确定,比如:

    bcdUSB 代表 USB 版本号,比如 0x0110,代表 USB 1.1 版本(bcd编码,即写成十六进制时的版本号),这样主机就知道这个设备只支持全速 12 Mbit/s 那么关于关于高速的请求qualifier(wValue 高字节为6)就不用发送到该设备了,因为发送给设备也肯定会被回复 STALL,那么主机就没必要浪费这个带宽了。

    但是如果你这里写成 0x0200,那么这个设备可能是全速的,也可能是高速的,那么主机就会发送请求来询问是否支持高速,如果设备不支持,回复的描述符设置为0即可。

    接下来的三个字节根据设备属于什么类别来确定,比如 CDC 类,这三个值分别为 0x02 ,0x00,0x00。

    bMaxPacketSize0 确定了端点0的数据包大小,主机可以据此知道设备的传输能力,进而控制传输数据包的大小,不然主机一次发送的数据包太大,那么从机可能无法正确接收。

    idVendor 由 USB-IF 分配,这个值确定了这个设备属于哪个厂家的产品。比如 0xC251,代表了KEIL,只要主机看到了这个代号,就知道这个设备由哪个厂家生产的了,因为这个在USB-IF中挂了号,大家都可以从网上查到。

    和必须购买的 idVendor 不同却类似的是,iProduct 是由厂家自己定义的,可以根据这个来确定这个设备属于哪个产品。

    这个USB设备更新到哪一个版本了?通过bcdDevice 即可确定,也是bcd 编码。

    iManufacture 代表厂商的字符串序号,一般都是 1,这样当主机需要获取厂商的字符串,只要在wValue 的字节为设置为 1,那么从机就知道该发送什么字符串给主机了。

    iProject 代表产品字符串序号,一般为2。

    iSerialNumber 代表产品序列字符串序号,一般为3。

    为什么从 1 开始编号,而不是从 0 开始呢,这是因为如果设备没有这个字符串的话,可以设置该值为 0,这样主机就知道没有这个字符串,也就不会主动获取这个字符串。

    当然了,即使你告诉了主机有这个字符串存在,主机也是按照需求来获取的,不一定会把所有的字符串描述符都获取回来。

    iNumConfigurations 代表了设备有多少种配置,前面说过,设备可能会在不同时刻的功能表现不一样,那么可以通过该值确定这个设备有多少种配置,一般而言这个值是1,即只有一种配置。毕竟复合设备可以同时满足多功能的要求,没必要使用多种配置来达到多种功能的要求。

    以上就是设备描述符的具体含义,其他描述符比如配置描述符、接口描述符、端点描述符等就自行看鱼鹰给的资料理解即可,只要找到对应描述符的格式说明,分析代码中的描述符数据也不是那么难的事情。

    接下来鱼鹰介绍枚举总体流程。

    主机在对设备复位后,首先会请求获取设备描述符。这个描述符一般为18个字节,但是主机一开始并不知道这个描述符多大(虽然一般是18,但万一不是呢),所以一般主机会以更大的请求长度来获取,而从机根据实际长度18字节返回即可。

    现在我们从多个维度看看这次交互的数据情况:

    从传输事务的角度看:

    从包的角度看:

    从DATA内容看:

    主机发送数据:80 06 00 01 00 00 40 00

    从机回复数据:

    从D+、D-数据线电平变化的角度:

    主机发送(建立阶段):

    从机回复(数据阶段):

    状态阶段:

    现在把整个枚举过程大概图解一遍(其他请求交互的具体情况请看鱼鹰提供的资料):

    数据流截取(鱼鹰提供的《CDC设备完整数据通信.txt》):

    展开全文
  • 来源:公众号【鱼鹰谈单片机】作者:鱼鹰OspreyID :emOsprey上篇笔记,大家了解了一些枚举相关的概念,本篇笔记将详述 CDC 设备枚举过程,让大家对整体的枚举过程有个概念。为了更好到理解并分析接下来的通信流程,...

    来源:公众号【鱼鹰谈单片机】

    作者:鱼鹰Osprey

    ID   :emOsprey

    上篇笔记,大家了解了一些枚举相关的概念,本篇笔记将详述 CDC 设备枚举过程,让大家对整体的枚举过程有个概念。为了更好到理解并分析接下来的通信流程,鱼鹰首先介绍标准请求描述符。上篇笔记了解了标准请求和描述符是怎么回事,但还不够,还需要更细节的东西。首先从标准请求的8字说起:ac891a9e4b6cb0a2afc12a8fe4a67928.png第一字节:位图请求信息。D7 代表了接下来传输的数据是从主机到从机,还是从机到主机的。我们知道枚举过程使用控制传输,有三个阶段,如果有数据阶段,那么这个阶段的DATA数据是由主机发出还是由从机发出,就看这个位的值了。当然了,因为每一次事务都有令牌包存在,所以IN令牌后的数据一定是由从机发出的,但是标准请求的这个位可以让从机做好发送数据或者接收数据的准备。94c3ef14c8c31435b1ce018e5712e8f5.pngD6~D5:代表了请求类型。请求类型代表本次请求属于什么类型的请求。目前有三类,标准、类、厂商。标准请求主要有:5f6ee913fdd2c29d05c81945c0de0c9a.png除了标准请求,还有类请求,比如 CDC 类,用到了三种类请求:SET_LINE_CODING(0x20)          设置串口波特率、起始位、停止位、流控等信息GET_LINE_CODING(0x21)        获取串口波特率、起始位、停止位、流控等信息SET_CONTROL_LINE_STATE(0x22)用于设置串口的状态厂商请求一般不会用于标准设备,CDC 类就没有用到(如果需要的话,应该也是能发出的)。D4~D0:代表了请求类型。因为请求的内容可能是面向设备,也可能面向接口、端点,所以这个域确定了本次请求面向的对象,这样设备可以根据请求的对象作相应的措施。字节:bRequest请求代码,即上面的几种请求代码,每个请求都会有请求代码,代表了具体请求。三四字节:wValue这个双字节主要根据bReuest来确定含义,比如如果是获取描述符(GET_DESCRIPTOR),而描述符有很多种,比如设备描述符、字符串描述符、配置描述符,那么到底主机要获取什么描述符?就看这个双字的高字节了。如果字节为 1,代表获取设备描述符,高字节为 2,代表获取配置描述符。总之这个值的具体含义需要根据请求代码来确定,而每一种请求代码都会明确规定wValue具体含义。五六字节:wIndex这个值和上面一样,也是需要请求代码来确定含义的。比如在获取产品序列号字符串时,这个值代表了语言 ID,告诉从机需要返回什么哪种字符串,当值为0x0409时代表英语。七八字节:wLenth这个值代表接下来主机会发送或者需要接收字节长度。一般来说,主机会根据需要在接下来的数据阶段获取或发送指定长度数据,主机发送的数据因为是由主机控制的,所以可以很容易确定这个值,但是因为主机并不清楚从机到底有多少数据会返回,所以这个值可能会比实际的更大。比如第一次获取设备描述符时,因为主机不清楚这个描述符多长,一般会比实际的描述符长度更大,所以如果从机没有足够的数据返回,那么只要返回从机能返回的最大数据即可;但是如果主机请求返回的数据比从机实际的数据,那么从机就按照主机的要求来就行,不必把自己所有的数据返回。以上就是标准请求的内容。设备返回的描述符通用格式比较简单:第一字节:描述符总长度(包括本字节)第二字节:描述符类型(对应标准请求wValue的高字节)其余字节就代表了这个描述符的具体内容了,每种描述的具体内格式都不相同,需要根据实际的描述符确定,比如:51769049735d875ac68061384438a37a.pngbcdUSB 代表 USB 版本号,比如 0x0110,代表 USB 1.1 版本(bcd编码,即写成十六进制时的版本号),这样主机就知道这个设备只支持全速 12 Mbit/s 那么关于关于高速的请求qualifier(wValue 高字节为6)就不用发送到该设备了,因为发送给设备也肯定会被回复 STALL,那么主机就没必要浪费这个带宽了。但是如果你这里写成 0x0200,那么这个设备可能是全速的,也可能是高速的,那么主机就会发送请求来询问是否支持高速,如果设备不支持,回复的描述符设置为0即可。接下来的三个字节根据设备属于什么类别来确定,比如 CDC 类,这三个值分别为 0x02 ,0x00,0x00。bMaxPacketSize0 确定了端点0的数据包大小,主机可以据此知道设备的传输能力,进而控制传输数据包的大小,不然主机一次发送的数据包太大,那么从机可能无法正确接收。idVendor 由 USB-IF 分配,这个值确定了这个设备属于哪个厂家的产品。比如 0xC251,代表了KEIL,只要主机看到了这个代号,就知道这个设备由哪个厂家生产的了,因为这个在USB-IF中挂了号,大家都可以从网上查到。和必须购买的 idVendor 不同却类似的是,iProduct 是由厂家自己定义的,可以根据这个来确定这个设备属于哪个产品。这个USB设备更新到哪一个版本了?通过bcdDevice 即可确定,也是bcd 编码。iManufacture 代表厂商的字符串序号,一般都是 1,这样当主机需要获取厂商的字符串,只要在wValue 的字节为设置为 1,那么从机就知道该发送什么字符串给主机了。iProject 代表产品字符串序号,一般为2。iSerialNumber 代表产品序列字符串序号,一般为3。为什么从 1 开始编号,而不是从 0 开始呢,这是因为如果设备没有这个字符串的话,可以设置该值为 0,这样主机就知道没有这个字符串,也就不会主动获取这个字符串。当然了,即使你告诉了主机有这个字符串存在,主机也是按照需求来获取的,不一定会把所有的字符串描述符都获取回来。iNumConfigurations 代表了设备有多少种配置,前面说过,设备可能会在不同时刻的功能表现不一样,那么可以通过该值确定这个设备有多少种配置,一般而言这个值是1,即只有一种配置。毕竟复合设备可以同时满足多功能的要求,没必要使用多种配置来达到多种功能的要求。以上就是设备描述符的具体含义,其他描述符比如配置描述符、接口描述符、端点描述符等就自行看鱼鹰给的资料理解即可,只要找到对应描述符的格式说明,分析代码中的描述符数据也不是那么难的事情。接下来鱼鹰介绍枚举总体流程。主机在对设备复位后,首先会请求获取设备描述符。这个描述符一般为18个字节,但是主机一开始并不知道这个描述符多大(虽然一般是18,但万一不是呢),所以一般主机会以更大的请求长度来获取,而从机根据实际长度18字节返回即可。现在我们从多个维度看看这次交互的数据情况:从传输事务的角度看:08087d7c05c574532d8afd798b380447.png从包的角度看:16ba02d24ac66d5797f4d06fe93feeed.png从DATA内容看:主机发送数据:80 06 00 01 00 00 40 00b5c858d65ae6fed06e2849e67b90b593.png从机回复数据:a6c12b5209392d3e1dd55f2e3504d4dc.png从D+、D-数据线电平变化的角度:主机发送(建立阶段):4ec5d7de2a85d46414b750cdcddc10a1.png从机回复(数据阶段):185a3ff29ce5fbc3fd9b1dd3f34f742d.png状态阶段:2819f6fb915ab9199c029afcd7e7af40.png现在把整个枚举过程大概图解一遍(其他请求交互的具体情况请看鱼鹰提供的资料):2b89d954a6554ed277d5a92ba44447e4.png数据流截取(鱼鹰提供的《CDC设备完整数据通信.txt》):1079da5a59c06b3c50622e98d249c4ba.png
    展开全文
  • usb驱动开发之USB协议枚举过程详解

    千次阅读 2017-09-08 09:30:34
    本文把usb枚举过程通过文字、程序和图形三种形式描述出来,并形成对照。 一 枚举过程之文字描述  •主机集线器监视着每个端口的信号电压,当有新设备接入时便可觉察。(集线器端口的两根信号线的每一根都有15kΩ...

    当usb插入之后,主机是如何识别usb的,并且对它做了什么?这个过程专业术语就叫枚举。本文把usb的枚举过程通过文字、程序和图形三种形式描述出来,并形成对照。

     枚举过程之文字描述

             •主机集线器监视着每个端口的信号电压,当有新设备接入时便可觉察。(集线器端口的两根信号线的每一根都有15kΩ的下拉电阻,而每一个设备在D+都有一个1.5kΩ的上拉电阻。当用USB线将PC和设备接通后,设备的上拉电阻使信号线的电位升高,因此被主机集线器检测到。)

       • 连接了设备的 HUB在 HOST 查询其状态改变端点时返回对应的 bitmap,告知HOST 某个PORT状态发生了改变。 
       • 主机向 HUB查询该PORT的状态,得知有设备连接,并知道了该设备的基本特性。  
       • 主机等待(至少 100mS)设备上电稳定,然后向 HUB 发送请求,复位并使能该PORT。 
       • HUB执行PORT复位操作,复位完成后该PORT就使能了。现在设备进入到defalut状态,可以从Vbus获取不超过 100mA 的电流。主机可以通过 0地址与其通讯。 
       1 主机通过0地址向该设备发送get_device_descriptor标准请求,获取设备的描述符。目的是取得却缺省控制管道所支持的最大数据包长度,该长度包含在设备描述符的bMaxPacketSize0字段中,其地址偏移量为7,主机读取64字节,但实际不一定能读到,因为这时候还不知道一次能读取的最大长度,但是肯定能读到前8个字节,因为可能的值为(8,16,32,64).
       • 主机再次向 HUB发送请求,复位该PORT。 
       2 主机通过标准请求 set_address给设备分配地址。 
       3 主机通过新地址向设备发送 get_device_descriptor标准请求,获取设备的描述符。 
       4 主机通过新地址向设备发送其他 get_configuration请求,获取设备的配置描述符。

       5 根据配置描述符的wTotalLength字段(地址偏移为2,总共两个字节,即偏移地址3表示高8位,偏移地址2表示低8位),表示该配置描述符及其包含的接口描述符、端点描述符和供应商描述等的总长度。英文原文:Total length of data returned for this configuration.Includesthe combined length of all descriptors (configuration, interface,endpoint,and class- or vendor-specific)returned for this configuration. 再次发送get_configuration请求,获取数据长度为wTotalLength。

       6 根据配置信息,主机选择合适配置,通过 set_configuration请求对设备而进行配置。这时设备方可正常使用。

    二 枚举过程之程序描述

    这是uboot中usb_hub.c和usb.c中精简之后的代码,可以大致看出整个过程。文字描述中的1-6都有对应的函数,前面几项描述由于uboot和linux内核的实现稍有差别,就不列出来了,只简单描述,读者自己可以查看源码。uboot是通过在命令行usb start,调用到do_usb()->usb_init()->usb_scan_devices()->usb_new_device(dev)->usb_hub_probe()->usb_hub_configure()->usb_hub_port_connect_change()。也就是说是插入u盘之后,手动输入命令,使用了一个查询过程。函数实现简单、清晰,易入手。

      内核源码也是在hub.c中,usb_hub_init()->kthread_run(hub_thread,NULL, "khubd")-> hub_thread-> hub_events()->hub_port_connect_change()。当有usb设备插入时,会唤醒hub_thread线程,从而调用hub_events执行,检测端口状态变化。

    void usb_hub_port_connect_change(struct usb_device *dev, int port)
    {
              /* 获取端口状态变化信息*/
              usb_get_port_status(dev, port + 1,portsts) ;
     
              /* Clear the connection change status 清除端口变化 */
              usb_clear_port_feature(dev, port + 1,USB_PORT_FEAT_C_CONNECTION);
     
            /*等待至少100ms,等待插入设备稳定*/
              mdelay(200);
     
              /* Reset the port 对设备的第一次操作,复位设备;对设备的第一次操作一定是复位。先复位,后获取
          *注意hub端口和usb设备是不同的操作,前面usb_get_port_status是对hub 端口的操作。*/
              hub_port_reset(dev, port,&portstatus) ;
             /*等待设备复位完成*/
              mdelay(200);
     
               /*好戏来了*/
              usb_new_device(usb);
             
    }
    int usb_new_device(struct usb_device *dev)
    {
            /*包大小先初始化一个值64*/
            dev->maxpacketsize = PACKET_SIZE_64;
            /*1 获取设备描述符,读取长度64*/
            err = usb_get_descriptor(dev,USB_DT_DEVICE, 0, desc, 64);
            /*获取最大包长度*/
            dev->descriptor.bMaxPacketSize0 =desc->bMaxPacketSize0;
     
            /* reset the port for the second time第二次复位设备 */
            err = hub_port_reset(dev->parent,port, &portstatus);
     
     
            dev->epmaxpacketin[0] =dev->descriptor.bMaxPacketSize0;
            dev->epmaxpacketout[0] =dev->descriptor.bMaxPacketSize0;
     
            switch(dev->descriptor.bMaxPacketSize0) {
            case 8:
                   dev->maxpacketsize  = PACKET_SIZE_8;
                   break;
            case 16:
                   dev->maxpacketsize =PACKET_SIZE_16;
                   break;
            case 32:
                   dev->maxpacketsize =PACKET_SIZE_32;
                   break;
            case 64:
                   dev->maxpacketsize =PACKET_SIZE_64;
                   break;
            }
            dev->devnum = addr;
     
            /*2 设置设备地址*/
            err = usb_set_address(dev); /* setaddress */
     
            mdelay(10);    /* Let the SET_ADDRESS settle */
     
            tmp = sizeof(dev->descriptor);
     
            /*3 用新设置的地址获取设备描述符*/
            err = usb_get_descriptor(dev,USB_DT_DEVICE, 0, tmpbuf, sizeof(dev->descriptor));
     
            memcpy(&dev->descriptor, tmpbuf,sizeof(dev->descriptor));
     
            /* only support for one config for now*/
            /*4 和 5封装成了一个函数 获取配置描述符*/
            usb_get_configuration_no(dev, tmpbuf,0);
            usb_parse_config(dev, tmpbuf, 0);
            usb_set_maxpacket(dev);
            /* we set the default configuration here*/
           /*6 配置设备*/
            usb_set_configuration(dev,dev->config.desc.bConfigurationValue);
     
            return 0;
    }
    int usb_get_configuration_no(struct usb_device *dev, unsigned char *buffer, int cfgno)
    {
    	struct usb_configuration_descriptor *config;
    	config = (struct usb_configuration_descriptor *)&buffer[0];
    	/*4 获取配置描述符,仅配置描述使长度为9*/
    	result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 9);
    	/*当前配置下的描述符总长度*/
    	tmp = le16_to_cpu(config->wTotalLength);
    	/*5 再一次获取当前配置下的全部配置、接口、端点信息*/
    	result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, tmp);
    	return result;
    }

    三  枚举过程之图形描述

    枚举的总过程,看过下面的分解过程,再回头看这个总图就非常容易了。


    1. 获取设备描述符GET_DESCRIPTOR

          总线复位及向默认地址0发送GET_DESCRIPTOR指令包,请求设备描述。第一获取描述符要先复位设备,然后等待至少100ms(100ms可以满足大多数设备),这里实际只等待了43ms。如图一所示:


                                                                                        图一

    1)Index[4 - 5]:表示USB插入总线复位
    2)Index[7 - 8]:表示主机向默认地址发送GET_DESCRIPTOR指令包,详细信息也抓出来了,如图所示:

                                                                      
    3)Index[15 - 17]:表示设备向主机发送设备描述数据Index[16]
    4)Index[18 - 19]:表示主机完成GET_DESCRIPTOR指令后,给设备发送一个空应答;
     
    数据是由二进制数字串构成的,首先数字串构成域(有七种:同步域(SYNC)、标识域(PID)、地址域(ADDR)、端点域(ENDP)、数据域(DATA)、帧号域(FRAM)、校验域(CRC)),域再构成包(比如握手包:格式如下  SYNC+PID,ACK属于PID的一种),包再构成事务(IN、OUT、SETUP,每一种事务都由令牌包、数据包、握手包三个阶段构成),事务最后构成传输(中断传输、并行传输、批量传输和控制传输)。关于usb包结构,请看下一篇文章,这里先知道是这么回事就行。
    这里先解析一下设置事务的值:80 06 00 01 00 00 40 00
    /* device request (setup) */
    struct devrequest {
    unsigned char requesttype;
    unsigned char request;
    unsigned shortvalue;
    unsigned shortindex;
    unsigned shortlength;
    } __attribute__ ((packed));
    requesttype=0x80,request=0x06,value=0x0100,index=0x0000,length=0x0040;
    根据usb2.0协议9.4.3节描述,获取描述符时requesttype=0x80,request=0x06,这个是协议规定的。
    value=0x0100,高字节表示描述符类型,01表示设备,02表示配置;低字节表示索引。比如设备有多个配置,那需要读取不同配置的时候就通过低字节。或者一个配置下有多个接口,通过索引选择不同的接口。
    index=0x0000,这个参数如果为0,则不关心;如果为非零,则表示Langurage ID,每一位都有对应的意义。
    length=0x0040,表示请求的数据包长度为64.
    所以本次设置事务的目的明确了,获取设备描述符,长度为64字节。
    输入事务,是usb设备对请求进行回应,传输了16个字节的数据,为什么是传输了16个字节而不是64字节,看看偏移地址7 bMaxPacketSize0=0x10,即该设备的最大包传输大小为16字节,如果超过16字节,需要多次传输。实际设备描述符大小为18,可以看第三步的图,传输完16字节之后,主机再次发送令牌环,设备把剩下的2字节传输完成。这里我们只要获得 bMaxPacketSize0值就可以了,所以接下来直接对其进行了复位操作,否则设备还在等待传输剩余的2个字节呢。
    struct usb_device_descriptor {
    u8 bLength;
    u8 bDescriptorType;
    u16 bcdUSB;
    u8 bDeviceClass;
    u8 bDeviceSubClass;
    u8 bDeviceProtocol;
    u8 bMaxPacketSize0;
    u16 idVendor;
    u16 idProduct;
    u16 bcdDevice;
    u8 iManufacturer;
    u8 iProduct;
    u8 iSerialNumber;
    u8 bNumConfigurations;
    } __attribute__ ((packed));
    输出事务:因为获取描述符之前把设备包大小的初始值设为了64,所以输入事务之后,就认为传输完成,主机发送了一个输出事务,响应设备,已经收到数据。

    2. 设置地址SET_ADDRESS

         再次复位总线及向设备发送SET_ADDRESS指令包,设置设备地址。如图二所示:
     
                                                                                         图二
    1)Index[22 - 23]:表示再次总线复位,该复位自动完成,不是手工插拔USB完成
    2)Index[25 - 27]:表示主机向默认地址发送SET_ADDRESS指令包,详细信息如图所示:
                                               
    设置地址为1

    3)Index[29 - 31]:表示设备完成SET_ADDRESS指令后,给主机发送一个空应答;


    设置地址为0x0002,由于我是从网上找的图,所以两幅图地址设置的不一样,这里注意一下就行。

    3.请求设备描述符GET_DESCRIPTOR

        向第二步设定的设备地址发送GET_DESCRIPTOR指令包,请求设备描述。如图三所示:
     
                                                                                          图三
    1)Index[33 - 35]:表示主机向地址01发送GET_DESCRIPTOR指令包,详细信息如图
                                                                   
    2)Index[41 - 43]:表示设备向主机发送设备描述数据Index[42]
    3)Index[45 - 47]:表示设备向主机发送设备描述数据Index[46]
    4)Index[48 - 50]:表示主机完成GET_DESCRIPTOR指令后,给设备发送一个空应答
      

    4. 请求配置描述符GET_DESCRIPTOR

    向第二步设定的设备地址发送GET_DESCRIPTOR指令包,请求配置描述。如图四所示:
     
                                                                                      图四
    1)Index[52 - 54]:表示主机向地址01发送GET_DESCRIPTOR指令包,详细信息如图所示
                                                           
    2)Index[57 - 59]:表示设备向主机发送配置描述数据Index[58]
    3)Index[60 - 62]:表示主机完成GET_DESCRIPTOR指令后,给设备发送一个空应答;
     
    解析输入事件获取的配置信息:
    struct usb_configuration_descriptor {
    u8 bLength;                      /*09,描述符长度为9*/
    u8 bDescriptorType;/* 0x2, 表示配置描述符*/
    u16 wTotalLength;          /*0x002E,表示当前配置下的各种描述信息总长度为46*/
    u8 bNumInterfaces;        /*0x01,当前配置下共一个接口*/
    u8 bConfigurationValue; /*0x01,当前配置索引,当设置某一配置时,给SetConfiguration(x)传递的参数*/
    u8 iConfiguration;/*0x00,字符串描述索引*/
    u8 bmAttributes;/*0x60,属性信息*/
    u8 bMaxPower;/*0x01当前配置最大消耗电流*/
    } __attribute__ ((packed));

    5. 读取完整设备描述及配置描述

    再次读取配置描述符,读取长度46字节。
     
        09 02 2E 00 01 01 00 60 01 和第四步获得的一样
        09 04 00 00 04 00 00 00 00 描述符长度也是09,04表示是一个接口描述符
        07 05 81 03 08 00 c8 描述符长度为07,05表示端点描述符
        07 05 01 03 08 00 c8
        07 05 82 02 40 00 00
        07 05 02 02 40 00 00
    接口描述符详解:
    struct usb_interface_descriptor {
    u8 bLength;                     /*0x09,描述符长度*/
    u8 bDescriptorType;/* 0x04 ,表示接口描述符*/
    u8 bInterfaceNumber;    /*0,接口号为0*/
    u8 bAlternateSetting;    /*0,接口的可选设置*/
    u8 bNumEndpoints;     /*04,当前接口共4个端点*/
    u8 bInterfaceClass;       /*0*/
    u8 bInterfaceSubClass; /*0*/
    u8 bInterfaceProtocol;  /*0*/
    u8 iInterface;                /*0*/
    } ;
    struct usb_endpoint_descriptor {
    u8 bLength;                     /*07,描述符长度*/
    u8 bDescriptorType;/* 0x5 ,表端点描述符*/
    u8 bEndpointAddress; /*0x81,bit[7] =1,表示输入端点,0表示输出端点;bit[6:4],保留;bit[3:0]端点号,为1*/
    u8 bmAttributes; /*0x03,bit[1:0]=11,表传输类型为中断传输*/
    u16 wMaxPacketSize; /*0x0008,当前端点最大发送和接收包大小*/
    u8 bInterval; /*0xc8,查询时发送数据的间隔时间*/
    } ;

    6 配置设备SET_CONFIGURATION

           向第二步设定的设备地址发送SET_CONFIGURATION指令包,设置配置描述。如图六所示:
     
                                                                         图六
    1)Index[139 - 141]:表示主机向地址01发送SET_CONFIGURATION指令包,详细信息如下:
                                                
    2)Index[143 - 145]:表示设备完成SET_CONFIGURATION指令后,给主机发送一个空应答
    至此,枚举过程结束,设备可通过驱动与主机通信了。
     
    /* device request (setup) */
    struct devrequest {
    unsigned char requesttype;  /*0x00,表请求设置*/
    unsigned char request;        /*0x09,表示设置配置*/
    unsigned shortvalue;   /*0x0001,使用配置,必须匹配从配置描述符都过来的bConfigurationValue*/
    unsigned shortindex;   /*0x0000,协议规定设为0*/
    unsigned shortlength;  /*0x0000,协议规定设为0*/
    } __attribute__ ((packed));

    四 总结

    下面举几个例子来说明USB的通讯过程:
    1:主机想要向设备传送一串数据。 过程如下:
    (1) 主机向从机发送 “令牌包”,令牌包的类型为输出包,表示主机要向从机发送数据了。
    (2) 主机向从机发送完令牌以后,USB处理器件根据发送的令牌,会将中断状态寄存器标志置位,从机CPU通过查询USB处理器件的中断状态寄存器,对主机的令牌包进行响应
    (3)从机判别出中断类型,于是,准备从主机接收数据。
    (4) 从机准备好了,于是主机开始发送“数据包” 这时,USB处理器件会自动将从主发送过来的数据放如它的内部缓冲区内,接收完这个数据包后,从机向主机发送“应答包” 
    这就是一个完整的通讯过程。
    由以上可以看出,USB若是想要传送数据,那么主机必须先发一个 IN 或OUT的令牌包,然后发送DATA0,或DATA1数据包。
    简单的用现实生活中的事件进行描述:  老板想让员工去做一件事情,老板 先会发出命令,告诉要做什么事情,员工准备好以后呢,老板再把做这件事情的经费发放给员工,当员工把发放的经费清点以后,发现数目正确,他会给老板一个回应信息,告诉老板,钱已经收到了,而且数目正确。
    老板想让员工做的事:  对应USB通讯里的令牌包。
    老板想要发放的经费:  对应USB通讯里的数据包。
    员工给老板的回应:    对应USB通讯里的握手包。
    这里尤其需要注意一个问题就是:
    USB主机向设备发送令牌包的时候,接收令牌是有USB器件来完成的,而不是有从机CPU来完成的,如主机发送一个如下的令牌:
    SYNC+PID+ADDR+ENDP+CRC5
    USB器件回根据PID的类型来判断是哪种类型的令牌 根据ADDR的值来判断是否是和自己通讯,根据ENDP的值来判断是和哪个端点进行通讯,根据校验来判断,数据传送是否无误。根据以上的令牌包信息,USB器件会将其内部的中断状态寄存器相应的位置位,从机CPU可以查询这个中断状态寄存器来进行相应的操作。





    展开全文
  • 来源:公众号【鱼鹰谈单片机】作者:鱼鹰OspreyID :...在鱼鹰提供的例程资料中,标准请求通过一个结构体体现:以下是CDC枚举过程涉及到的所有标准请求和对应的描述符:获取设备描述符:设置地址:这次事务从...

    来源:公众号【鱼鹰谈单片机】

    作者:鱼鹰Osprey

    ID   :emOsprey

    虽然鱼鹰提供的资料已经把图解资料分享出来了(Osprey文件夹),但因为这个系列就是图解系列,所以还是把 CDC 枚举过程中涉及到的所有标准请求和描述符都展现一下好了。在鱼鹰提供的例程资料中,标准请求通过一个结构体体现:a79c81607a402617db2a495f375cd850.png以下是CDC枚举过程涉及到的所有标准请求和对应的描述符:获取设备描述符:f8a8fdf6bef6248ab466bf3780adb19d.png设置地址这次事务从机不需要回复或接收数据,所以没有数据阶段,只有状态阶段。283dec79cb2ab44040e19cf13684e0a6.png获取配置描述符:这个描述符最长,如果只是单纯的获取配置描述符的话,其实没有多少,但实际上,主机大部分情况下会一次性要求返回包括配置描述符、接口描述符、端点描述等描述符。这样主机才能根据当前的配置描述符确定接口和端点信息。因为接口和端点描述符依赖配置描述符的信息,所以从机需要把所有信息一次提供给主机,这样可以保证数据的完整性(否则可能在交互时该配置下的描述符和别的配置下描述符错乱,毕竟 USB 可是支持多种配置的)。这里面比较难理解的是Union相关描述符,简单理解就是,因为CDC有两个接口,而主机一般会把每一个接口当做一种设备功能,所以为了让主机知道这里面的两个接口其实是一个设备功能,所以使用Union 等描述符来告诉主机两个接口属于同一个功能,可以使用一个驱动程序驱动。91f1f25bc8879b8a9d3c1d975bc5bb59.png6a02856d6e7e41c18d8e4a07002d8089.pngdcea92b9ef5ebae72e94b42c22ed932b.png字符串描述符,主机显示用:cb8623a3aff53f7ace354141d28c3969.png获取语言ID26265842098b1ec2ee37b5210559eb88.png4e946d754fec88ebf7414e9a9d4df3f8.png3e39645ab0a8c870e84c82ac81cf09f1.pngb6f609031fb5f8ae71a88204af1397e2.pngf138696b1773bd0257dc30c0ad3d706c.png关于字符串,鱼鹰提供的例程并不能在【设备管理器】中正确显示,可能就是因为语言ID 返回 0x0409, 表示ASCII编码,但是实际上却是 Unicode 编码,这可能就是为什么设备可以正常使用,但字符串显示不对的原因了。推荐阅读:嵌入式系统优先级详解KEIL 调试经验总结线程CPU使用率到底该如何计算?许久以后,你会感谢自己写的异常处理代码终极串口接收方式,极致效率延时功能进化论(合集)如何写一个健壮且高效的串口接收程序?打了多年的单片机调试断点到底应该怎么设置?| 颠覆认知

    -THE END-


    如果对你有帮助,记得转发分享哦

    微信公众号「鱼鹰谈单片机

    每周一更单片机知识

    7a3769072e879e172d8f43ecbc5b8c9d.png

    长按后前往图中包含的公众号关注

    鱼鹰,一个被嵌入式耽误的畅销书作家

    个人微信「EmbeddedOsprey

    0d5b9ad199d5729207940696254b521a.png

    长按后打开对方的名片关注

    展开全文
  • 在鱼鹰提供的例程资料中,标准请求通过一个结构体体现: 以下是CDC枚举过程涉及到的所有标准请求和对应的描述符: 获取设备描述符: 设置地址: 这次事务从机不需要回复或接收数据,所以没有数据阶段,只有状态阶段...
  • 1.设备上电 2.检测电压变化,报告主机 3.主机了解连接设备 4.检测所插入的设备是高速还是低速 5.主机通过hub复位设备 6.主机进一步检测全速设备是否是支持高速模式 7.通过Hub建立主机和设备之间的信息通道 8....
  • USB详解(3.枚举

    2020-03-16 12:10:59
      枚举就是主机获取从机的信息(各种描述符)用来加载不同的驱动,告诉主机要怎么控制、通信,主机检测到从机上线之后会进行枚举的流程,枚举USB中最重要也最复杂,前面说到USB传输有四种模式,控制传输最繁琐但...
  • STM32 USB HID 枚举

    2020-05-05 15:53:56
    一个USB设备只能有一个设备描述符 /* USB Standard Device Descriptor / const uint8_t Joystick_DeviceDescriptor[JOYSTICK_SIZ_DEVICE_DESC] = { 0x12, /bLength 1个字节/ USB_DEVICE_DESCR...
  • USB学习笔记----枚举部分详解说明:本详解是基于USB1.1协议的,参考ZLG的《D12 USB固件编程与驱动开发》。主要说说其枚举过程中所传输的数据。硬件自动完成的部分就不提。详见书P90。1、0x80 0x06 0x00 0x01 0x00 0x...
  • USB 之二 USB2.0 规范详解 第二部分

    千次阅读 2018-04-20 14:23:01
    注意 对于物理特性仅做简单说明 ...  USB 设备第一次连接到主机时, 要接收主机枚举( Enumeration) 和配置(Configuration) ,目的是让主机知道设备功能、是哪一类的USB 设备、占用多少资源、使用了...
  • 我会写的非常详细且简洁,后面在学习USB枚举的时候会通过抓包和波形来学习每一个描述符在总线上的作用,并且会介绍每一个描述符是在什么时候以哪种方式在总线上进行传输的,今天主要是学习USB设备描述符的组成。...
  • 基于usb描述符详解

    2011-07-20 13:52:00
    详细介绍usb枚举阶段所需的设备描述符、配置描述符、接口描述符、端点描述符
  • stm32f4 USB项目开发详解

    千次阅读 2019-09-27 08:17:52
    主机开始对设备枚举(根据枚举得到的各种信息加载合适的驱动程序,比如根据信息知道是一个鼠标设备,则加载鼠标的驱动程序对接下来的数据进行处理) 枚举完成后主机要发送令牌包(IN / OUT)查询有效端点是否有数据...
  • 前言  暂且将枚举过程分为两个阶段。    第一个阶段我称其为通性阶段,就是只要是USB设备都会经历... 第二阶段,我称其为个性阶段,因为大多数USB设备在完成上一步骤,枚举过程就已经结束了,严格来说,该阶...
  • 1.其实所有的描述符都是USB设备用来描述自己属性及用途的,所以必须在设备端实现对应的描述符,主机会在枚举此设备的时候根据设备实现的描述符去确定设备到底是一个什么样的设备、设备需要的总线资源、和设备的通讯...
  • usb的旅途之枚举

    2011-12-08 22:14:09
     USB命令与设备描述符之开发详解 首先列出枚举过程中抓的数据包     /***************************************************************************/ Bus Hound 5.00 capture. Complements of w
  • USB详解资料整理.zip

    2019-06-14 11:53:58
    USB开发大全_第4版 圈圈教你玩USB高清版 USB应用技术开发大全 VC USB编程 :可以枚举所有设备
  • 详解USB控制传输

    2020-12-08 17:30:21
    usb枚举过程是通过控制端点0使用控制传输方式来实现对usb 设备信息的获取和配置的,相对于其他传输方式,控制传输过程相对麻烦些,这里进行下说明。 一、USB控制传输 1 传输过程 usb 控制传输分为三个过程,分别为...
  • USB多点触控上报协议详解

    千次阅读 2020-01-18 17:00:36
    USB HID-compliant mouse报点协议解析 ...1、当你的usb hid设备枚举正常后,你可以使用bus hound看到你的usb 设备里有一个新增的HID-compliant mouse接口。可以通过usbTreeView查看对应的端点,我这里的该...
  • USB 协议规范本身有几百页的内容,这还不包括各种设备类等子文档,但我觉得USB协议基础部分除了需要了解一下USB物理传输模型外,最重要的内容是USB的... USB设备从插入主机到建立通讯要经历两个阶段:1、USB设备...
  • USB-设备描述符(一)

    2020-01-16 14:56:24
    USB鼠标详解阅读顺序 1、枚举 2、设备描述符 3、设置地址 4、配置描述符 5、接口描述符 6、HID 描述符 7、端点描述符 8、字符串描述符 9、HID 报告描述符 10、HID 报告的返回 linux内核中对设备描述...
  • 1.USB设备请求 2.USB描述符详解 3.USB的控制传输详解 4.USB协议物理层总结 5.USB协议中的建立事务 6.HID描述符详解-en 7.USB枚举过程 8.HIDUsageTables 9.USB_HID协议中文版_USB接口HID设备 11.传输线终端阻抗匹配 ...
  • USB入门基础(协议)

    2011-07-29 09:58:00
    USB入门基础 1、USB概述 2、USB的连接模型[图] ...6、USB设备枚举过程 7、USB的描述符及各种描述符之间的依赖关系 8、USB HID报告及报告描述符简介 9、USB HID 设备类协议入门 10、USB描述符详解 11、USB标准请求命令
  • USB_STM32_HID开发笔记:里面有USB设备枚举的详细过程,抓包说明的哦。当然包括开发过程啦。令牌包、握手包、数据包中的数据都看得到。 USB_URB分析:抓包工具抓到的数据包的详细解析。 USB_VC教程:短论文一篇,用...
  • USB HID STM32 开发资料全集

    热门讨论 2012-12-17 12:09:10
    USB_STM32_HID开发笔记:里面有USB设备枚举的详细过程,抓包说明的哦。当然包括开发过程啦。令牌包、握手包、数据包中的数据都看得到。 USB_URB分析:抓包工具抓到的数据包的详细解析。 USB_VC教程:短论文一篇,用...
  • USB-设置地址(二)

    2020-01-16 15:17:14
    USB鼠标详解阅读顺序 1、枚举 2、设备描述符 ...从USB 枚举章节我们知道,获取到 USB设备描述符之后就会对设备再次进行复位,然后进入设置地址阶段 例如,一个 USB鼠标的设备地址过程如下所示 ...
  • Linux USB 详解[转]   Gadget USB协议[转]__总结得很好  ...一 枚举过程: ...◆ 用户将一个USB设备插入USB端口,主机为端口供电,设备此时处于上电状态。 ◆主机检测设备。 ◆集线器使用中断通道将事
  • USB-配置描述符(三)

    2020-01-16 15:53:37
    USB鼠标详解阅读顺序 1、枚举 2、设备描述符 3、设置地址 4、配置描述符 5、接口描述符 6、HID 描述符 7、端点描述符 8、字符串描述符 9、HID 报告描述符 10、HID 报告的返回 linux内核中对配置...
  • USB-端点描述符(六)

    2020-01-16 16:58:43
    USB鼠标详解阅读顺序 1、枚举 2、设备描述符 3、设置地址 4、配置描述符 5、接口描述符 6、HID 描述符 7、端点描述符 8、字符串描述符 9、HID 报告描述符 10、HID 报告的返回 linux内核中对端点...
  • USB-接口描述符(四)

    2020-01-16 16:09:53
    USB鼠标详解阅读顺序 1、枚举 2、设备描述符 3、设置地址 4、配置描述符 5、接口描述符 6、HID 描述符 7、端点描述符 8、字符串描述符 9、HID 报告描述符 10、HID 报告的返回 linux内核中对接口...

空空如也

空空如也

1 2
收藏数 40
精华内容 16
关键字:

usb设备枚举详解