尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

USB 设备描述符 配置描述符 报表描述符

USB 设备描述符 配置描述符 报表描述符 来学习一下 USB 的各种描述符 ...... 矜辰所致前言对于刚接触 USB 的小伙伴来说如果一开头直接去啃 USB 协议那应该会是一个痛苦的过程。对于应用来说我们可以选更加快速合理的方式先把接口使用起来在后期需要往深层研究的时候再去仔细了解对应的资料。那要快速的应用第一个务必掌握的内容就是 USB 通信中的各种描述符所以本文我们的目的就是了解一下 USB 使用中的各种描述符。我是矜辰所致全网同名尽量用心写好每一系列文章不浮夸不将就认真对待学知识的我们矜辰所致金石为开目录前言一、 基础说明1.1 什么是描述符1.2 什么时候用到描述符枚举过程二、 描述符解析2.1 设备描述符2.2 配置描述符2.2.1 配置描述符2.2.2 接口描述符2.2.3 类描述符2.2.4 端点描述符2.3 报表描述符三、 示例修改结语一、 基础说明在讲解 描述符之前我们首先得知道在 USB 中描述符是一个什么角色出现在 USB 通信中的什么地方。1.1 什么是描述符在 USB 世界里描述符类似于设备的 身份证 能力清单 。它是一张写好的数据表主机靠读取 描述符 才能知道 是什么设备、能干什么、需要多少电、用哪个驱动。1.2 什么时候用到描述符枚举过程至于 描述符 什么时候用到我们就有必要来了解一下 USB 主机与从机的通信过程。USB 设备从插入上电、被主机检测到到主机为其分配唯一地址、获取全部描述符并加载对应驱动使设备进入可用状态的全过程称为枚举。USB 设备交互流程如下此处我们以我们的 PC 为示例电脑的USB接口 D-D默认为低电平当有 USB 设备插入的时候会使得 D- 或 D 引脚电平拉高以此电脑判断该 USB 设备的速率此时只能判断是低速还是全速高速还需要在后面确认。此时主机会等待至少 100ms 等待设备稳定。主机发出复位信号把 D/ D- 拉低至少10 ms在这段时间里如果从设备是高速设备从设备会先发一个 1 ms~7 ms 的 Chirp K800 mV 脉冲高速主机自己能跑高速。如果主机也支持高速会马上回一串 KJ/KJ… 序列确定好后双方同时切到高速模式若主机不回应设备保持全速。主机获取设备描述符 主机为从设备分配唯一设备地址后续通讯用此地址 主机获取配置描述符 主机为设备选择合适的配置设备进入“已配置”状态可以开始类协议CDC/MSC/HID…根据不同的设备类型主机获取字符串、报表、接口关联描述符等此步骤不是枚举固定流程是可选流程枚举完成 – 驱动加载进入正常通信。给想全面学习 USB 的伙伴推荐一个视频教程本文部分截图都出自与此视频教程是沁恒微官方账号发布的 《USB技术应用与开发》教程很详细的https://space.bilibili.com/489340606/lists/896957关于基础的部分本文就介绍到这里我们下面就结合示例来说明分析一下这几个描述符。二、 描述符解析我们讲解的描述符的顺序是按照 USB 枚举的流程来的同理在不同的带 USB 接口芯片上的示例工程中描述符的定义基本也都是按照枚举顺序来定义描述符数组的。本文以 沁恒微 Ble 芯片 CH585 USB 示例为说明选取的示例为2.1 设备描述符设备描述符是主机最先获取的它固定为 18 个字节示例中定义如下#defineDevEP0SIZE0x40#defineDevEP1SIZE0x40// 设备描述符constuint8_tMyDevDescr[]{0x12,0x01,0x10,0x01,0x00,0x00,0x00,DevEP0SIZE,0x3d,0x41,0x07,0x21,0x00,0x00,0x00,0x00,0x00,0x01};设备描述符是如何定义的可以参考下面的表格偏移长度字段名含义01bLength固定 0x1218 十进制本消息长 18 字节11bDescriptorType固定 0x01告诉主机“我是设备描述符”22bcdUSBUSB 版本小端USB2.00x0200USB1.10x011041bDeviceClass整个设备属于哪一大类。0 根据后面的接口来判断0xFF 表示“厂商自己定”51bDeviceSubClass上面大类的子类通常跟着 class 走61bDeviceProtocol再细分协议0 表示没有特殊协议71bMaxPacketSize0端点 0 最大包长只能 8/16/32/64主机靠它决定后续控制传输一次发多少超过会分包82idVendor厂商 ID小端沁恒的为 0x1A86示例使用的是自定义的 0x413D102idProduct产品 ID小端 自己定义122bcdDevice设备固件版本号小端BCD 码0x0100 代表 1.00141iManufacturer“厂商名字”在字符串描述符里的索引0 表示“没有”151iProduct“产品名字”在字符串描述符里的索引161iSerialNumber“序列号”在字符串描述符里的索引171bNumConfigurations设备总共提供几种配置1 表示有多种配置获取设备描述符示意图如下上面关于 idVendor 厂商 ID 可通过下面网址查询 https://devicehunt.com/search/type/usb/vendor/1A86/device/any对于上面我们示例中定义的来说我们在 PC 端识别出来查看效果如下 2.2 配置描述符配置描述符 描述了设备特定的配置提供了当前配置下设备的功能接口供电方式耗电等信息。它是一个配置的集合集合长度不固定包含配置描述符接口描述符类定义描述符端点描述符。他们的先后顺序 USB 是有明确规范的配置描述符头 →第一个接口的接口描述符 →该接口的所有类特定描述符 →该接口的所有端点描述符 →下一接口的接口描述符 →…配置描述符示例中定义如下// 配置描述符 #define DevEP1SIZE 0x40constuint8_tMyCfgDescr[]{0x09,0x02,0x29,0x00,0x01,0x01,0x04,0xA0,0x23,//配置描述符0x09,0x04,0x00,0x00,0x02,0x03,0x00,0x00,0x05,//接口描述符0x09,0x21,0x00,0x01,0x00,0x01,0x22,0x22,0x00,//HID类描述符0x07,0x05,0x81,0x03,DevEP1SIZE,0x00,0x01,//端点描述符0x07,0x05,0x01,0x03,DevEP1SIZE,0x00,0x01//端点描述符};2.2.1 配置描述符定义中的第一行为配置描述符固定的 9 个字节解释如下0x09,0x02,0x29,0x00,0x01,0x01,0x04,0xA0,0x23, //配置描述符偏移字段长度值含义0bLength10x09头本身长度1bDescriptorType10x02配置描述符类型固定02 表示我是配置2wTotalLength2配置返回的数据总长度小端 0x29把每一行的第一个字节相加因为每一行的描述符第一个字节表示长度99977 0x294bNumInterfaces1本配置里接口个数示例为1鼠键复合设备就填 25bConfigurationValue11~255主机用这条索引“激活”我Get Configuration 和Set Configuration请求的配置值6iConfiguration10 或字符串索引给人看的配置名字可选7bmAttributes1位7 总线供电位6 自供电位5 远程唤醒置位位1表示支持8bMaxPower1电流值/2 mA设备从总线获取的最大功耗0x23 → 70 mA2.2.2 接口描述符我们前面讲过配置描述符的顺序是有规范的配置头 →接口 0 描述符 →接口 0 类描述符如果前面声明自己是 HID/音频/CDC →接口 0 的各端点 →接口 1 描述符 →接口 1 类描述符 →接口 1 的各端点 →…现在就轮到接口描述符了也是固定的9个字节解释如下0x09,0x04,0x00,0x00,0x02,0x03,0x00,0x00,0x05, //接口描述符偏移字段名长度值含义0bLength10x09固定 9字节长度1bDescriptorType10x04接口描述符类型 固定 0x042bInterfaceNumber10x00本接口编号从 0 开始3bAlternateSetting10x00备用设置号非音频一般 04bNumEndpoints10x02本接口下的端点个数不含 EP05bInterfaceClass10x03HID 类 0x03U 盘 0x08串口 CDC-Control 0x02串口 CDC-Data 0x0A摄像头 0x0E自定义 0xFF6bInterfaceSubClass10x00HID 子类0普通 HID 设备报表描述符完全自定义0x01 兼容 BIOS 的键盘/鼠标7bInterfaceProtocol10x00协议码0 无引导协议普通 HID需要自定义报告0x01键盘Boot Keyboard—— BIOS 能用0x02鼠标Boot Mouse—— BIOS 能用8iInterface10x05字符串索引给界面看名字0无键盘bInterfaceClass0x03bInterfaceSubClass0x01bInterfaceProtocol0x01鼠标bInterfaceClass0x03bInterfaceSubClass0x01bInterfaceProtocol0x022.2.3 类描述符当接口描述符 声明自己是 HID/音频/CDC 类型的时候接口描述符后面必须跟着 类描述符由接口描述符中bInterfaceClass字段决定0x03 → HID必须紧跟 1 个 HID 描述符bDescriptorType0x21再跟端点0x02 → CDC-Control必须紧跟 Header/Union/Call-Management 等 functional descriptorsbDescriptorType0x24再跟端点0x0E → Video必须紧跟 VC/VS 描述符bDescriptorType0x24/0x25再跟端点0x0A → CDC-Data没有类描述符直接跟端点0x08 → Mass-Storage没有类描述符直接跟端点本文以 HID 类描述符说明也是固定的9个字节解释如下0x09,0x21,0x00,0x01,0x00,0x01,0x22,0x22,0x00, //HID类描述符偏移字段名长度值含义0bLength10x09长度固定 9 字节1bDescriptorType10x21HID 类描述符固定 0x212bcdHID20x00 0x01HID 规范版本小端BCD 0x0101 → 1.014bCountryCode10x00硬件设备所在国家的国家代码0 通用/无国家键盘布局5bNumDescriptors10x01后面紧跟的“下级描述符”个数≥1类别描述符数目至少有一个报表描述符6bDescriptorType210x22类别描述符的类型0x22 报表描述符Report基本上鼠标、键盘、自定义 HID 都只用到 0x227wDescriptorLength20x22 0x00报表描述符总长度小端0x0022 34 字节2.2.4 端点描述符接下来轮到端点描述符端点描述符固定为 7个字节接口描述符中定义了有几个端点这里就需要写几个端点的描述符解释如下0x07,0x05,0x81,0x03,DevEP1SIZE,0x00,0x01, //端点描述符0x07,0x05,0x01,0x03,DevEP1SIZE,0x00,0x01 //端点描述符偏移字段名长度值含义0bLength10x07长度固定 71bDescriptorType10x05端点描述符类型固定 0x052bEndpointAddress10x81位7方向(1IN)位3…0端点号示例 0b10000001 端点1的INEP1 IN0x01 EP1 OUT0x82 EP2 IN3bmAttributes10x03位1…0传输类型0x03中断4wMaxPacketSize20x40 0x00最大包长低字节 0x0040 → 64 B6bInterval10x01查询端点进行数据传输的间隔轮询间隔 1 帧(全速1 ms)对于上面的bEndpointAddress字段:bit 7bit 6…4bit 3…0DIR保留(0)端点号1 表示 IN 端点Device → Host0 表示 OUT 端点Host → Device0端点号015)端点 0 被默认控制端点独占这里不能出现。对于上面的bmAttributes字段在我另一篇文章:CH585 高速 USB模拟 CDC串口应用示例中有对应的说明到这里配置描述符的相关说明就讲完了小结一下对于配置描述符图片示意如下2.3 报表描述符报表描述符相对前面的描述符来说就复杂了它和前面的描述符不一样并没有几个字节的固定格式。我们先来看一下官方的示例中报表描述符定义/*字符串描述符略*//*HID类报表描述符*/constuint8_tHIDDescr[]{0x06,0x00,0xff,0x09,0x01,0xa1,0x01,//集合开始0x09,0x02,//Usage Page 用法0x15,0x00,//Logical Minimun0x26,0x00,0xff,//Logical Maximun0x75,0x08,//Report Size0x95,0x40,//Report Counet0x81,0x06,//Input0x09,0x02,//Usage Page 用法0x15,0x00,//Logical Minimun0x26,0x00,0xff,//Logical Maximun0x75,0x08,//Report Size0x95,0x40,//Report Counet0x91,0x06,//Output0xC0};虽然报表描述符没有固定字节数但是它有固定的语法全是 1 字节前缀 0~ 4 字节数据的“短 Item” 组成。每一个 Short Item 解析方式在官方文档中有说明表格如下位段含义取值与作用bit7~bit4Tag指令标识0x00Usage Page、0x01Usage、0x08Input、0x0CEnd Collection …共16个bit3~bit2Type指令类型00Main、01Global、10Local、11保留bit1~bit0Size参数字节数000字节、011字节、102字节、114字节例0x81 0x02 → TAG8(Input), TYPE0(Main), SIZE1 → 后跟 1 字节位属性我们虽然知道了解析但是我们好像又遇到了新的知识点上面的 Type 和 Tag 是什么东西里面的 GlobalLocalMain 又是什么感觉有好多种类挺麻烦的…确实完全熟悉这些东西需要一定的时间而且大家不需要慌张我们实际应用的时候一般都是使用现成的报表描述符修改而且也通过官方文档表格查找对应的组合而且现在 AI 也可以帮我们生成我们知道了如何解析了解基本的含义使用起来就问题不大。USB 官方资料下载地址https://www.usb.org/hid有两个文档有我们需要的全部内容想要深入的小伙伴可以自己去阅读《Device Class Definition for HID》《HID Usage Tables》第一份文档告诉你“怎么拼 Item”第二份告诉你“拼完以后数据填什么常量”。本文我们就简单的提一下上面 Type 和 Tag 必须成对出现才有意义Type 定 “作用域”Tag 定 “具体功能”USB HID 规范已经把每种有效组合都预先定义好了。常见的组合如下Type 编码 (bit3-2)作用域大类Tag 编码 (bit7-4)具体指令00Main8Input00Main9Output00Main10Feature00Main11Collection00Main12End Collection01Global0Usage Page01Global1Logical Min01Global2Logical Max01Global3Physical Min01Global7Report Size01Global8Report Count01Global9Report ID10Local0Usage10Local1Usage Min10Local2Usage Max比如0x05 0x01 → TypeGlobal, TagUsage Page, Size1 字节参数 0x01 表示“Generic Desktop”页0x75 0x08 → TypeGlobal, TagReport Size, Size1 字节参数 0x08 表示“字段位宽 8 bit”我们现在可以通过解析知道他们组合表示什么但是组合实际的意义也是需要花时间讲清楚大家目前可以自行查看文档学习这个后面有机会单独再写一篇关于 HID 报表描述符的文章说明把。对于示例的报表描述符我们也挑几个简单解释一下0x06, 0x00,0xff → TypeGlobal, TagUsage Page, Size2数据 0xFF00 厂商自定义页Global 表示后面所有字段先去 0xFF00 这张表查含义.0x09, 0x01 → TypeLocal, TagUsage, Size1数据 0x01厂商自定义 Usage-1Local 表示只给下一条 Main 用.0xA1, 0x01 → TypeMain, TagCollection, Size1数据 0x01 : 开“Application”级集合表示“一个独立应用功能整体”例如“键盘应用”“鼠标应用”“消费者控制应用”.0x09, 0x02 → TypeLocal, TagUsage, Size1数据 0x02厂商自定义 Usage-2Local 表示又一个 Local 贴签留给下一条 Main.0x15, 0x00 → TypeGlobal, TagLogical Min, Size1Global 对“整列”生效作用到下一个 Global 为止Logical Min 设“逻辑最小值”数据 0x00逻辑最小值 0.0x26, 0x00, 0xFF → TypeGlobal, TagLogical Maximum, Size10b2Logical Maximum设“逻辑最大值”数据 0x00FF逻辑最大值 255.0xC0 → End Collection关闭当前最里层集合与最近的 0xA1 对应关闭离它最近、且尚未被 0xC0 关闭的那条 0xA1有点像一个大括号{} 。三、 示例修改我们最后做个简单的修改让电脑识别我们的开发板为一个键盘设备。我们修改过后的描述如如下具体的解析大家可以自己根据上面解析比较我们这里只说几个主要的点上面代码不需要修改示例代码的基本框架我们只修改了描述符。首先是报表描述符这个我是从另外一个示例拿的现成的我们在后期应用的时候基本上也很少手敲报表描述符基本都是拿现成的示例做自己需要的修改就可以了。报表描述符改了它的长度变了那么就需要把 类描述符的wDescriptorLength字段最后2个字节改成它对应的长度也就是0x3e, 0x00,;然后因为是键盘我们在上面说过在接口描述符中键盘bInterfaceClass0x03bInterfaceSubClass0x01bInterfaceProtocol0x01我们需要把对应的地方修改好这样就已经把我们的设备改成一个键盘类型的设备了如下图当然上面我们的修改描述符只是让 PC 把开发板识别成一个键盘至于数据交互那是需要我们在 USB 中断中进行处理的这个后期有机会可以写文章说明不是本文讨论范围。结语本文主要说明了一下 USB 的不同的描述符我们做 USB 应用了解了各描述符的解析可以尝试自己去修改描述符就是一个很好的开始那就等于学会了一半 哈 (✿◡‿◡) 。好了本文就到这里。谢谢大家
返回列表