尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

深入理解USB设备VID/PID:从原理到ST-Link“unknown device ID”排查

深入理解USB设备VID/PID:从原理到ST-Link“unknown device ID”排查 做嵌入式开发和硬件调试的朋友十有八九都撞上过这个画面Keil里点完DownloadST-Link那一栏弹出一行红字unknown device id紧接着就是一堆莫名其妙的错误。第一次遇到时我怀疑过目标板短路、怀疑过芯片烧了、怀疑过ST-Link坏了折腾了半天最后才发现问题其实出在最基础的两个东西上——Vendor ID与Device ID。这两个ID一个代表设备厂商一个代表具体设备型号是所有USB外设通信的第一道门禁。搞清楚它们是什么、怎么查、怎么用很多调试问题都能少走一大半弯路。这篇文章适合刚接触USB设备开发、嵌入式调试的初学者也适合那些被烧录器报错折磨过、想系统梳理排查思路的硬件工程师。我会从VID/PID的原理讲起再给出一套从Windows到Linux都通用的查询方法接着结合驱动开发、固件设计、烧录调试等真实场景最后用ST-Link报unknown device ID这个典型问题串起一整个排查实录。内容尽量做到“不仅告诉你怎么做还告诉你为什么要这么做”。1. 先搞懂Vendor ID与Device ID到底是什么很多人第一次接触Vendor ID和Device ID都是在设备管理器里看到一串类似USB\VID_0483PID_3748的字符串。当时只觉得是一串随机字符完全没想过这背后是一套完整的设备身份标识体系。理解这套体系是后面所有排查和开发工作的地基。1.1 USB世界的身份证VID/PID的来源与分配机制在USB协议里每个设备都要向主机上报两个16位的十六进制值分别是idVendor和idProduct。idVendor叫Vendor ID也就是厂商ID它由USB规范的管理机构USB-IF统一分配和管理全世界不能重复。idProduct叫Product ID也叫Device ID由厂商自己在内部定义用来区分同一厂商下的不同设备型号。换句话说Vendor ID是“你是哪家的”Device ID是“你家的哪款产品”。举个大家熟悉的例子ST意法半导体的Vendor ID是0x0483它的ST-Link调试器的Device ID可能是0x3748、0x374B或0x374DSTM32虚拟串口的Device ID是0x5740。FTDI的Vendor ID是0x0403它的FT232串口芯片Device ID是0x6001。如果你在设备管理器里看到USB\VID_0403PID_6001几乎可以立刻断定这是一个FT232方案的USB转串口模块。这个机制本质上和我们的身份证体系很像身份证号前缀由公安机关编码相当于Vendor ID个人序列号相当于Device ID。USB-IF把Vendor ID发给厂商后厂商怎么分配Device ID完全自由但同一个Vendor ID下的Device ID最好保持全局唯一否则就会出现驱动匹配混乱。除了VID和PID设备描述符里还有一个bcdDevice字段代表设备版本号用来区分同一型号的硬件修订版本这在驱动兼容性判断时非常有用。1.2 从设备描述符看VID/PID的枚举流程USB设备插上主机后不是立刻就能用的中间有一个“枚举”过程。主机先给设备复位然后在地址0向设备发送GET_DESCRIPTOR请求设备收到后会把设备描述符返回给主机。设备描述符是一个固定格式的数据结构里面包含了bLength、bDescriptorType、bcdUSB、idVendor、idProduct、bcdDevice、bMaxPacketSize0等字段。主机读到这些字段后才知道插上来的是一个什么设备、该找哪个驱动来匹配。这个过程很像酒店前台办理入住你递上身份证前台从系统里一查就知道你叫什么、从哪来、属于哪个协议单位然后给你分配对应的房卡。设备描述符里的VID/PID就是前台系统用来识别的“身份证号”。如果设备返回的VID/PID是0x0000或者一个没有注册的ID操作系统就只能把它当“未知设备”处理驱动匹配自然失败。有些人可能会好奇为什么有些设备管理器里看不到VID/PID只能看到“未知USB设备设备描述符请求失败”这通常意味着设备的枚举过程在更早阶段就出了问题主机连设备描述符都没有拿到自然拿不到VID/PID。这种情况大多数是硬件层的问题比如D上拉电阻缺失、USB线损坏、设备供电异常等后面讲排查时会重点展开。1.3 VID/PID冲突会导致什么后果VID/PID虽然只是两个16位数字但它们一旦冲突后果非常棘手。我自己就踩过一次当时做了一个基于STM32的自定义HID设备为了省事直接复制了官方评估板的VID和PID结果插到电脑上以后Windows给设备装了官方评估板的驱动导致我的设备一直以错误的方式被打开。这种问题在设备管理器里很难看出来因为设备名显示正常但实际通信就是不对。另一种常见冲突场景是“复制粘贴式开发”。很多学习板、开发板出厂时固件里直接写死了厂商的VID/PID后来者拿这些代码改产品忘记修改这两个值导致市面上的设备VID/PID大量重复。Windows的驱动缓存机制会记住设备第一次插入时匹配的驱动如果不同设备共用了同一个VID/PID就可能出现“换一台电脑驱动装不上”或者“设备间歇性失灵”的情况。所以我在做USB设备开发时把“确定自己的VID/PID”列为项目启动的第一件事甚至先于画原理图。量产产品需要向USB-IF申请Vendor ID个人学习或小批量内部工具则可以用一个自定义的测试VID但一定要保证不和常见厂商ID冲突比如用0xFFFF加自己定义的PID同时做好记录避免项目之间互相覆盖。2. 查询Vendor ID与Device ID的几种实用姿势知道原理之后最关键的就是怎么快速查到设备的VID/PID。这里分享几种我平时最常用的方法从Windows到Linux都覆盖到大家可以根据手头的环境选择。2.1 Windows下三分钟查到VID与PIDWindows下最直接的方法是打开设备管理器。按Win X选择“设备管理器”展开对应设备分类比如“端口COM和LPT”或“通用串行总线设备”右键设备选择“属性”切到“详细信息”页签在下拉框里选“硬件ID”。这时候就能看到类似USB\VID_0483PID_374BMI_00或USB\VID_1A86PID_7523的字符串VID后面跟的就是Vendor IDPID后面跟的就是Device ID。这个方法零成本、不需要装任何工具但缺点是只能看到十六进制数字看不到厂商名称。如果你想一眼看出这个设备是谁家的可以用NirSoft的USBDeview这个小工具绿色免安装能列出所有USB设备的名称、VID/PID、序列号、驱动状态。它是查看USB设备信息的“瑞士军刀”调试USB问题非常方便。还有一个进阶技巧如果设备已经装不上驱动在设备管理器里连具体型号都显示不出来可以打开注册表编辑器定位到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB下面会以VID_xxxxPID_yyyy为键名列举所有曾经连接过的USB设备。即使设备已经被拔掉历史记录也可能还在这里。这个方法对排查“这台电脑之前插过什么设备”特别管用。2.2 Linux下用lsusb一步到位Linux下查VID/PID就简单多了在终端里输入lsusb输出会列出当前所有USB设备的总线编号、设备编号、厂商ID、产品ID以及设备名称。比如Bus 001 Device 004: ID 0483:374b STMicroelectronics ST-LINK/V2.1这里的0483是Vendor ID374b是Device ID冒号后面那一串就是设备描述符里上报的产品名。如果需要更详细的信息比如设备的bcdDevice版本号、接口描述符、端点信息可以加-v参数执行lsusb -v。注意-v输出非常长通常需要root权限建议配合管道过滤比如lsusb -v -d 0483:374b只查看指定设备。如果想通过系统文件确认也可以直接读取/sys/bus/usb/devices/下面的目录每个设备目录里有idVendor、idProduct、manufacturer、product等属性文件用cat命令逐个查看即可。如果是在嵌入式Linux板卡上调试这些命令不一定都有但busybox里通常包含lsusb或者你可以用cat /proc/bus/usb/devices查看老内核可能还保留着这个接口。总而言之查看USB设备ID在Linux下是几分钟的事情不存在什么门槛。2.3 查询VID归属用官方数据库确认厂商有时候你会看到一个新的VID设备管理器里显示正常但就是不知道是哪家厂商。这时候可以到USB-IF官网查询已分配的Vendor ID列表官方提供的表格支持CSV下载导入Excel后可以按VID快速筛选。国内访问有时稍慢也可以用linux-usb.org维护的usb.ids文件这是一个公开维护的USB厂商与设备ID数据库Linux的lsusb就用它来解析厂商名称。usb.ids文件格式很简单十六进制VID顶格写下面缩进的十六进制PID是对应型号。平时做驱动开发或者内核debug时如果发现设备名显示不出来可以检查一下系统里的/usr/share/hwdata/usb.ids是否过旧更新到最新版再插拔一次设备很多人名就能正常显示出来。这个小细节在量产阶段和售后分析时非常实用。3. Vendor ID与Device ID在实际开发中的应用场景查询只是基本功真正考验人的是把VID/PID用在驱动开发、固件设计和设备调试中。这里我挑三个最常见的场景展开配合代码示例和踩坑记录读者可以直接抄作业。3.1 Linux内核驱动匹配从USB_DEVICE宏说起在Linux下写一个USB设备驱动核心工作是构造一个usb_driver结构体并填充id_table成员声明这个驱动支持哪些设备。id_table是一个struct usb_device_id数组每一行可以通过USB_DEVICE宏来快速初始化。比如要支持VID为0x0483、PID为0x374B的ST-Link设备代码就是USB_DEVICE(0x0483, 0x374B)。USB_DEVICE宏的本质是把idVendor和idProduct填到usb_device_id结构体里同时设置匹配标志。内核在枚举USB设备时会把设备报告上来的VID/PID与所有驱动的id_table逐一比对命中后调用probe函数设备才算被驱动接管。这里有一个容易被忽略的细节如果设备固件里改了PID而驱动程序里的id_table没同步更新插上设备后内核会提示“找不到驱动”但设备本身在lsusb里是正常的。所以每次改固件一定要同步检查驱动侧的匹配表。在实际项目里同一颗芯片通过不同固件实现不同功能的情况很常见比如同一个硬件既是HID设备又是串口设备。这样的设计下建议给不同固件分配不同的PID这样驱动层可以在probe里根据PID分支处理避免同一PID下行为混乱。如果PID不够分配还可以用bcdDevice版本号或接口描述符做二次区分但结构上会复杂不少。3.2 Windows驱动与INF文件里的VID/PID匹配逻辑Windows下设备驱动的匹配依据是INF文件里的Install节。典型的驱动安装信息里会有一段类似%DeviceName% USB_Install, USB\VID_1234PID_5678的声明表示这个驱动只负责匹配VID为0x1234、PID为0x5678的设备。设备管理器在安装驱动时会遍历系统驱动库找到与硬件ID匹配的INF条目然后执行对应的安装脚本。这里最常遇到的问题有两个。一个是“驱动已安装但设备管理器还是黄色感叹号”这通常是因为设备上报的VID/PID和INF里的硬件ID对不上比如固件默认带的是0x0483而你的INF写的是0x1234。解决方法是重新确认设备管理器里显示的硬件ID字符串逐字符对齐INF条目注意MID、MI_00这类附加后缀的影响。另一个是“同一设备ID匹配到了两个驱动”Windows只会选择签名和时间戳更新的那个建议开发调试阶段把所有旧版本驱动卸载干净。做驱动开发的朋友还要记住一个原则INF文件里的硬件ID是设备枚举时上报的原始字符串大小写不敏感但格式必须完全匹配。很多新手会在INF里写USB\VID_1234PID_5678但设备上报的是USB\VID_1234PID_5678MI_00接口级别的驱动需要加MI_00后缀或者用通配符否则就会挂载失败。这一类问题用USBPcap或Wireshark抓枚举包能看到完整字符串排查效率会高很多。3.3 嵌入式设备自定义VID/PID的正确姿势如果你在做自己的USB设备固件肯定要在代码里设置VID/PID。以STM32的USB Device库为例设备描述符定义在usbd_desc.c里里面有一个USBD_VID和USBD_PID的宏改这两个宏就能改变设备上报的ID。有些HAL库版本还会在usbd_desc.c里定义USBD_LANGID_STRING、USBD_MANUFACTURER_STRING等字符串描述符这些字段也会影响设备在操作系统中的显示名。自定义VID/PID最容易犯的错误是直接沿用评估板的数值。评估板的VID/PID属于芯片原厂你用这个ID生产设备法律上有侵权风险实际使用也会和原厂驱动冲突。如果只是学习或者自用可以选一个未被分配的测试VID段比如0xFFFF再配上自己定义的PID。但量产产品必须向USB-IF申请正式的Vendor ID申请流程大致是加入USB-IF会员、缴纳年费、提交产品信息。很多中小公司嫌麻烦会选择使用芯片厂商提供的“量产专用VID”但这种方式受制于人后续设备标识不可控长远看还是自己申请更稳妥。另外提醒一点如果你的设备包含多个USB接口复合功能比如虚拟串口加调试接口每一个接口最好都用独立的PID加以区分或者至少保证设备描述符里的接口信息清晰可辨。Windows对复合设备的驱动匹配比单一功能设备要复杂接口描述符里的bInterfaceClass、bInterfaceSubClass、bInterfaceProtocol同样参与驱动选择设计时要把这些字段统一规划避免“设备ID对但功能不可用”的尴尬。4. ST-Link显示Unknown Device ID的排查全记录回到开头那个让无数人挠头的报错ST-Link烧录器显示unknown device id。这个“unknown device id”在不同软件里表现略有差异Keil里通常是“Unknown Device ID”STM32CubeProgrammer里可能是“Error: No STM32 target found”或“Unknown device ID”但本质都是同一个问题调试器没有从目标芯片读回预期的IDCODE。下面我会按“从主机到目标”的链路一步步拆解。4.1 先判断问题出在驱动层还是调试链路遇到报错不要急着拆线、换芯片第一步应该是判断故障范围。打开Windows设备管理器看ST-Link在不在。如果设备管理器里能看到“STMicroelectronics STLink dongle”或“ST-Link Debug”且没有黄色感叹号说明ST-Link与电脑之间的USB通信正常问题多半出在调试器与目标芯片之间的SWD链路或者目标板本身。如果设备管理器里显示的是“Unknown Device”或“USB Device Not Recognized”连ST-Link本身都没枚举成功那问题就在USB接口、数据线、ST-Link驱动或者ST-Link固件上。这一步判断非常关键因为它直接决定你是去折腾驱动还是去折腾目标板。我自己遇到过太多人拿着烙铁去焊线结果最后发现ST-Link插的USB口接触不良。还有一个容易被忽略的快速判断方法看ST-Link板上的指示灯。正常状态下ST-Link的红色电源灯会常亮绿色通信灯在下载调试时会闪烁。如果红灯不亮先怀疑USB线或USB口供电不足换根短线直插主板后置USB口再试。如果红灯亮、绿灯一直不闪则说明主机对ST-Link的通信支持可能有异常可以优先重装驱动或升级ST-Link固件。4.2 从Vendor ID入手驱动与枚举层面的排查如果问题定位在USB枚举层也就是设备管理器里ST-Link异常我就按以下顺序处理。第一步是重新安装ST-Link驱动建议从ST官网下载STM32CubeProgrammer安装包它自带完整的ST-Link USB驱动安装时勾选驱动组件比单独下载ST-LINK Utility更省心。安装后拔插一次ST-Link看设备管理器是否恢复正常枚举。第二步是检查USB物理链路。ST-Link用久了USB座子的焊点容易松动线材内部断裂更是常见。换一根质量好的USB线插到主板原生USB 2.0口尽量避免通过USB Hub转接。我见过不少问题就是USB Hub供电不稳导致ST-Link枚举失败尤其是那种不带外部电源的便携Hub。如果换了线换了口还是unknown device拿USBDeview看一眼设备是否出现在USB总线上如果完全消失ST-Link硬件损坏的概率就很大了。第三步是升级ST-Link固件。ST-Link的固件可以通过STM32CubeProgrammer的“Firmware update”功能升级操作前先拔掉ST-Link与目标板之间的所有连接线只保留USB连接。升级完成后设备管理器里ST-Link的VID仍然是0x0483但设备描述符里的版本号会变化。如果升级失败ST-Link可能进入DFU模式此时可以按住板上的复位键重新插拔USB再尝试恢复固件。4.3 从Device ID入手目标板与SWD链路排查当设备管理器里ST-Link完全正常但烧录软件还是报unknown device id时问题就在目标板侧。这时候我最先看的就是ST-Link上的VTref指示灯也就是目标电压参考灯。ST-Link通过SWD接口的第1脚监测目标板的电压如果这个灯不亮说明ST-Link根本没检测到目标板供电。很多情况下就是目标板没上电、供电开关没开、或者SWD排线没把VCC引脚连上。VTref正常后再检查SWD接线。SWD调试只需要四根线SWDIO、SWCLK、GND外加VCC用于参考电压检测部分场景可以不接VCC。接线时注意不要把SWDIO和SWCLK接反这种错误在自行设计的SWD排针上极其常见因为很多板子的丝印标识不清。还有一个容易踩的坑是线材过长或使用杜邦线飞线SWCLK频率较高时信号反射严重会导致下载不稳定或间歇性报错。如果接线无误但依然报unknown device id就要考虑目标芯片本身的状态。最常见的杀手是芯片读保护RDP被开启到Level 1甚至Level 2。芯片内的Flash被保护后SWD连接会受限调试器读不到正确的IDCODE。解决办法是用STM32CubeProgrammer连接芯片如果软件提示检测到读保护执行一次“Full chip erase”即可解除Level 1保护。但如果Level 2使能芯片将永久锁定只能换芯片。芯片的复位电路也是排查重点。STM32的SWD调试通常不依赖外部晶振但依赖芯片能正常上电复位。如果复位引脚被外部电路强行拉低或者复位电容失效芯片会一直处于复位状态自然无法响应调试器的IDCODE读取请求。手头有示波器的话可以量一下NRST脚的电压波形上电瞬间应该有一个由低到高的跳变。还有一个偏方按住目标板复位键点击下载或连接松手瞬间让芯片开始运行有时能绕过启动异常导致的调试失败。程序把SWD引脚复用成普通GPIO也会导致调试口失效这种情况在量产固件里特别常见。解决办法是先把BOOT0引脚拉高复位后让芯片进入系统存储器引导模式此时SWD引脚恢复正常再用调试器连接并擦除Flash。如果不想动BOOT0也可以试试“连接时按住复位键并在软件发出连接请求后立即松开”的技巧成功率因人而异但值得一试。最后一个可能性是芯片本身损坏或者虚焊尤其是手工焊接的板子芯片脚间距小连锡虚焊非常隐蔽。这种问题用肉眼很难看出来最快的验证方法是换一块已知正常的板子测试ST-Link再让目标板接一个确认可用的调试器用交叉排除法定位故障源。如果所有条件正常仍然报错建议把SWD时钟频率调低比如从4MHz降到1.8MHz或450kHz长期飞线调试时这个改动经常能救急。5. 常见问题速查表与避坑经验调试相关的问题往往重复性强把高频问题整理成一张速查表可以少走很多弯路。下面这个表格是我基于多年项目经验整理的高频问题与排查建议覆盖了从主机枚举到目标板的常见链路。故障现象可能原因处理建议设备管理器显示Unknown DeviceST-Link驱动未装或损坏重装STM32CubeProgrammer自带驱动设备管理器看不到ST-LinkUSB线损坏、口接触不良、硬件损坏换线换口使用USBDeview确认设备是否在线红灯不亮USB口供电不足或ST-Link硬件损坏使用主板原生USB口避免Hub转接红灯亮但软件报unknown device id目标板未供电、SWD接线错误、芯片锁死检查VTref指示灯核对SWDIO/SWCLK/GND接线下载时偶尔成功偶尔失败杜邦线过长、SWD信号反射、接触不良缩短线缆降低SWD时钟频率报错No STM32 target found目标芯片无供电、虚焊、损坏测量目标板3.3V交叉验证芯片芯片读保护提示RDP Level 1Flash被保护用STM32CubeProgrammer执行Full chip erase固件里SWD引脚被复用成GPIO程序占用调试口BOOT0拉高进入系统引导模式后擦除FlashST-Link固件升级后无法用固件升级中断或不兼容重新升级固件必要时恢复出厂固件目标板复位引脚一直为低复位电路故障、外部电路拉低量NRST波形检查复位电容和电阻5.1 几条非常实用的实操心得排查ST-Link报错这么多年我总结出几条对新手最友好的实操心得每一条都是真金白银换来的经验。第一手边常备一个USB电流电压检测小表头排查供电问题时非常高效。插上ST-Link或目标板后能直接看到当前电压电流比万用表去点引脚方便太多。目标板供电异常是最隐蔽的问题之一报错却能伪装成各种奇怪的现象包括VID/PID都识别不出来的情况。第二SWD连接线如果要用杜邦线尽量控制在10厘米以内并且SWDIO和SWCLK两条线不要并排靠太近。并排线之间会产生串扰高频时尤为明显。我的习惯是SWDIO和SWCLK之间隔一个GND线这样可以显著降低信号干扰。第三遇到芯片锁死不要慌先确认锁死级别。RDP Level 1可以全片擦除恢复RDP Level 2无法通过SWD恢复只能更换芯片。所以量产固件默认不开读保护是有道理的一旦开了售后维护会麻烦很多。第四养成“先看枚举再查链路最后怀疑芯片”的排错习惯。顺序错了会浪费大量时间。我在新手阶段总是一上来就怀疑芯片烧了结果换了好几颗芯片最后发现是ST-Link的排线座松了。后来学乖了先用设备管理器和USBDeview确认主机侧状态再做SWD信号测量。第五手头多备一个不同品牌的调试器。比如J-Link和ST-Link都备着遇到ST-Link无法识别目标芯片时换J-Link试试看。如果J-Link能连上说明目标板没问题问题出在ST-Link上如果J-Link也连不上那就是目标板侧的故障了。交叉验证在硬件调试里是最高效的手段。最后再说一个很多人不知道的小技巧STM32CubeProgrammer的日志输出里连接目标芯片成功后会打印读取到的芯片IDCODE比如STM32F103的IDCODE是0x1BA01477。如果你能看到IDCODE但随后又报错说明芯片已经被识别只是后续通信不稳定如果连IDCODE都读不到那才真正是unknown device id。把“能不能读到IDCODE”作为一条分界线整个排查思路会清晰很多。这个内容后续还可以往更深处扩展比如通过USBPcap抓取USB枚举包分析设备描述符的每一个字节或者自己从头写一个USB设备描述符模拟器。但今天分享的这些已经足够应对九成以上的VID/PID相关问题和ST-Link调试报错了。搞懂这两个ID你会发现自己对USB设备、驱动调试和嵌入式开发的理解都上了一个台阶。
返回列表