很多搞嵌入式的朋友应该都见过这个画面:ST-Link烧录器插到电脑上,Keil里一点Download,直接弹出一行红字,什么Unknown device ID、Target not found,设备管理器里再一看,好家伙,一个带黄色感叹号的未知设备。这时候你要是会看Vendor ID和Device ID,排查起来会快很多,而不是一脸懵地拔插线、换杜邦线、重启电脑。
这篇东西我就把这两个ID从原理、查询方法到实际用途一次讲透,最后用ST-Link报unknown device id这个典型场景做一次完整排查演示。不管你是在校学生刚接触单片机,还是天天跟各种开发板、烧录器打交道的工程师,这篇文章都能让你对USB设备识别这事有个系统认识。
1. Vendor ID与Device ID到底是个啥
1.1 USB设备的两张身份证
Vendor ID直译过来就是厂商ID,缩写VID,是USB标准化组织USB-IF分配给每家硬件厂商的唯一编号。Device ID是产品ID,缩写PID,由厂商自己在VID下面自由分配,用来区分自家不同型号的设备。
打个生活化的比方:VID相当于“身份证号前几位代表户籍地”,PID相当于“同一个人名下的不同手机号”。一个USB设备插到电脑上,系统首先读到的就是这两个ID,靠它们判断这设备是谁家生产的、具体是什么型号,然后决定加载哪个驱动程序。
从USB协议层面看,这两个ID是设备描述符(Device Descriptor)里的固定字段,位于描述符的偏移8到11字节处。Windows、Linux、macOS的USB驱动栈在设备插入时都会解析这段描述符,把它作为设备识别的第一依据。
1.2 VID/PID的分配规则与潜规则
VID不是谁想用就用的。正规路径是向USB-IF申请,非营利组织或者个人开发者可以低价甚至免费申请,但商用产品要缴纳年费,价格不便宜。所以很多小公司和个人开发者不会自己申请VID,而是直接用芯片原厂烧在固件里的VID。比如ST的STM32系列芯片,固件库里的USB设备描述符默认就带着ST的VID(0x0483),很多人做产品时就偷懒直接沿用。
这里就引出一个重要认知:在USB世界里,VID+PID组合只能说明“设备自称是谁”,不能绝对证明“它真的是谁”。山寨U盘、盗版ST-Link、仿制Arduino板,固件里照样可以写原厂的VID和PID。这也是后文ST-Link排查时会踩到的坑——设备管理器里明明显示VID_0483,但设备用起来就是不对劲。
1.3 为什么搞硬件的人必须懂这两个ID
我是真心建议所有做嵌入式、做硬件调试的人把VID/PID当常识来记。原因有三:
第一,驱动装不上时,唯一的排查入口就是硬件ID。设备管理器里未知设备的“硬件ID”属性,直接告诉你这个设备报的VID和PID是多少,对照它你才知道应该装哪个驱动。
第二,写上位机、写驱动、写烧录工具时,经常需要按VID/PID过滤设备。比如你用Qt写个串口助手,想只显示CH340串口,就得在枚举列表里按VID=0x1A86过滤。
第三,做USB设备开发时,你的设备能不能被系统正确识别,说到底就是看你发的设备描述符对不对。描述符里VID/PID写错了,驱动匹配全部跑偏。
2. 查询VID与PID的几种主流方法
2.1 Windows:设备管理器是最快路径
在Windows上查VID/PID,最常见的入口就是设备管理器。操作方式:右键“此电脑”选管理,进设备管理器,找到你的设备,右键属性,切到“详细信息”标签页,属性下拉框选“硬件ID”。
你会看到类似这样的字符串:
USB\VID_0483&PID_3748&REV_0100 USB\VID_0483&PID_3748VID_0483后面的四位十六进制数就是Vendor ID,PID_3748就是Product ID。REV字段是设备的版本号(BCDDevice),有时候驱动匹配也会用到它,但绝大多数情况下驱动INF文件只匹配VID和PID。
如果你嫌鼠标点来点去麻烦,可以用PowerShell:
Get-PnpDevice | Where-Object {$_.InstanceId -like "USB\*"} | Select-Object FriendlyName, InstanceId跑完直接列出所有USB设备的实例ID,一眼就能看到VID和PID。我自己调试时更喜欢用快捷键:Win+R输入devmgmt.msc,进去以后按设备类型展开,看到带感叹号的直接右键属性,整个流程不超过10秒。
2.2 Linux:lsusb一行命令搞定
Linux下查VID/PID是真的简单,终端敲一句lsusb直接输出:
Bus 001 Device 004: ID 0483:3748 STMicroelectronics ST-LINK/V2 Bus 001 Device 006: ID 1a86:7523 QinHeng Electronics CH340 serial converter冒号前面是VID,冒号后面是PID,最后是内核根据USB-IF数据库推断出来的厂商名和设备名。要注意这个名称仅供参考,它来自/usr/share/hwdata/下的usb.ids数据库文件,如果设备太新或者太冷门,可能显示“Unknown”。
想看得更详细,加个-v参数:
lsusb -v -d 0483:3748这条命令会打印完整的设备描述符信息,包括idVendor、idProduct、bcdUSB、iManufacturer等字段。做USB驱动开发的人应该把这条命令刻在脑子里。
另外补充一个进阶路径:直接看sysfs节点。Linux内核会把每个USB设备的信息暴露在/sys/bus/usb/devices/下,比如:
cat /sys/bus/usb/devices/1-2/idVendor cat /sys/bus/usb/devices/1-2/idProduct这在写脚本做自动化检测时非常有用,lsusb是给人看的,sysfs是给程序用的。
2.3 macOS:图形界面与终端双管齐下
macOS用户最简单的方式:左上角苹果图标→关于本机→系统报告→USB,里面会列出每个USB设备的厂商ID和设备ID。注意系统报告里显示的是十进制还是十六进制不一定,有时候需要自己在心里换算一下。
终端党可以用system_profiler:
system_profiler SPUSBDataType输出结构比较冗长,但信息很全,除了VID/PID还有USB版本、速度、电流需求等。再进阶一点可以用ioreg:
ioreg -p IOUSB -l -w 0能看到更底层的USB设备树信息,适合做驱动排查。
我把三种系统的查询方法列个表方便对照:
| 系统 | 常用命令/入口 | 关键信息位置 |
|---|---|---|
| Windows | 设备管理器→详细信息→硬件ID | USB\VID_xxxx&PID_xxxx |
| Windows | Get-PnpDevice(PowerShell) | InstanceId字段 |
| Linux | lsusb | ID xxxx:xxxx |
| Linux | cat /sys/bus/usb/devices/*/idVendor | sysfs节点 |
| macOS | system_profiler SPUSBDataType | 厂商ID/设备ID字段 |
| macOS | ioreg -p IOUSB -l -w 0 | idVendor/idProduct |
2.4 在线数据库:从ID反查设备身份
当你手上只有一个VID/PID,想反查这是什么设备时,有几个在线数据库非常实用:
- USB-IF官方数据库:
https://www.usb.org/developers,最权威但查询体验一般,适合查VID归属。 - devicehunt.com:界面干净,支持按VID/PID直接搜,会显示厂商名和设备名。
- linux-usb.org/usb.ids:Linux内核维护的IDS数据库,也是
lsusb背后的数据源,可以直接下载文本文件离线查。
实际工作中我的习惯是:先用lsusb或设备管理器拿到VID/PID,再丢到devicehunt里搜一下,基本能确定设备身份。有一次客户拿来一块自制板子,设备管理器里显示VID_1234&PID_0001,在线一查,VID_1234是某芯片原厂的,PID_0001完全查不到,立刻判断这是客户基于该芯片做的自定义设备,问题大概率出在设备侧而非驱动侧。这就是反查的价值。
3. VID/PID的实际应用场景
3.1 驱动匹配原理:Windows是如何“认人”的
Windows的驱动匹配机制本质上就是查表。设备插入后,系统枚举得到VID和PID,然后去系统驱动库里找匹配的INF文件。INF文件里用这样的语法声明自己支持哪些设备:
[Manufacturer] %MfgName%=DeviceList,NTamd64 [DeviceList.NTamd64] %DeviceName%=DriverInstall, USB\VID_0483&PID_3748这一行USB\VID_0483&PID_3748就是驱动与设备的“接头暗号”。如果INF里写的PID和你设备的PID对不上,哪怕设备芯片跟驱动完全兼容,Windows也坚决不装。遇到这种情况,最直接的解决办法有两个方向的思路:
第一个思路是改驱动INF,把设备的PID加进去,然后禁用驱动签名强制安装。这适合自用设备、内部工具,但正规产品绝对不建议这么干。改INF等于把驱动“硬掰”过去,能跑,但系统更新、驱动签名校验、WHQL认证这些后续问题会很难受。
第二个思路是改设备描述符里的PID,让设备“报出”一个和驱动匹配的PID。这就是做USB设备开发时的常规操作了。比如你做一块基于STM32的USB转串口板子,直接用ST官方的CDC类驱动,USB描述符里就要写对ST的VID和合适的PID,否则上位机枚举不到串口。
有个实操技巧值得记住:Windows驱动缓存里对同一个VID+PID+REV组合有记忆效应。如果你调试时频繁修改描述符里的PID,第一版驱动装的是旧驱动,第二版PID变了系统会再弹一次驱动安装,但有时候会莫名加载旧驱动。我踩过这种坑,后来习惯是调试期把设备的REV版本号每次递增一位,比如0100、0101、0102,这样Windows会认为它是新设备,驱动加载更干净。
3.2 设备区分:同型号多设备如何“分头管理”
插两个同型号的USB设备,比如两块CH340串口模块,Windows设备管理器里都叫“USB-SERIAL CH340”,单片机上电后你根本分不清哪个是COM3哪个是COM5。
这时候VID和PID帮不上忙,因为它们完全一样,得靠更底层的“设备实例路径”。在设备管理器里查看设备属性→详细信息→“设备实例路径”,会看到类似:
USB\VID_1A86&PID_7523\6&31ad27a5&0&3末尾那段6&31ad27a5&0&3跟USB端口号绑定,换了USB口就会变。所以如果你的工装要同时管理多个同型号USB设备,正确的做法是用SetupDiGetDeviceInstanceId这类API读取完整实例路径,按端口位置区分设备,而不是只按VID/PID匹配。
如果你用的是串口库,比如pyserial或Qt SerialPort,想自动找到指定端口还要再进一步,通过设备管理器里的“端口(COM和LPT)”节点,读它的“父系”属性里的链路信息来关联串口号和物理端口。这块展开讲能写一篇长文,但核心结论就一句:VID/PID用于识别设备“种类”,实例路径才是区分“个体”的钥匙。
3.3 USB设备开发:Vendor ID申请与使用技巧
自己开发USB设备,正式产品一定要有合法的VID。这里有三条路:
- 向USB-IF申请:非商业用途可以申请免费VID,商业用途要缴费,按年收费。流程不复杂,但周期要留够,从填表到拿到编号通常得几周到一两个月。
- 沿用芯片原厂VID:很多基于成熟芯片方案的产品就这么干。比如CH340芯片的方案,VID直接就是沁恒的0x1A86,固件里只能改PID。好处是不用申请,坏处是产品在系统里显示成别人的设备,品牌识别度为零,而且万一原厂哪天改驱动策略,你也被动受影响。
- 购买子授权VID:一些公司专门做VID授权生意,把自家VID下的多个PID以子授权形式卖给小公司与个人开发者,价格比直接申请便宜,但受制于人。这种模式下PID的选择空间有限,协议里可能限制你用在哪些产品类别上。
我个人的建议是:学生项目、自制工具、开源硬件用芯片原厂VID没问题,节省成本也方便;但任何想量产卖钱的产品,别省这笔钱,注册一个自己的VID是基本盘。否则你连换一颗主控芯片都得考虑驱动兼容性,产品迭代束手束脚。
做USB设备还有一个细节容易忽略:设备描述符里iManufacturer、iProduct字符串也会影响用户体验。Windows有时会在设备管理器里优先显示字符串描述符里的名字,而不是INF里的设备名,这时候如果字符串描述符写错了,即使VID/PID全部正确,设备管理器里看到的名称也莫名其妙。调试时可以先用USB树查看工具,比如USBTreeView,看一下芯片实际发出来的描述符内容和预期是否一致。
4. ST-Link报unknown device id的完整排查实录
4.1 先分清故障到底在哪一段
ST-Link烧录器报unknown device id,首先要判断是烧录器本身和电脑之间的USB链路出问题,还是烧录器和目标芯片之间的SWD链路出问题。这个判断是整个排查的分水岭。
操作很简单:把ST-Link插上电脑,打开设备管理器。看ST-Link在不在,驱动有没有叹号。
如果设备管理器里ST-Link压根不出现,或者显示为未知设备,问题在USB这一段。这时候立刻查看这个未知设备的硬件ID。如果硬件ID是USB\VID_0483&PID_3748,说明ST-Link的USB枚举是成功的,VID/PID正常,问题大概率在驱动上,重新安装ST-Link驱动即可。如果硬件ID是USB\UNKNOWN或者VID/PID全是0000,说明设备枚举失败,要么是ST-Link的USB线有问题,要么是烧录器硬件本身坏了,要么是山寨ST-Link的固件跑飞了。
如果设备管理器里ST-Link正常显示为“STMicroelectronics STLink dongle”或者“ST-LINK/V2”,那USB链路没问题,unknown device id的锅十有八九在SWD到目标芯片这一段。
4.2 USB链路正常,目标芯片连接失败的排查顺序
把目标板供电、ST-Link和目标板之间的连线确认一遍,按这个顺序走:
第一步,量目标板供电电压。ST-Link/V2自带3.3V输出,但电流非常有限,很多小板子上带LED、传感器,一跑起来电流就超了。先拿万用表量目标板VCC和GND之间有没有稳定3.3V。电压如果低于3.0V,MCU根本没法正常工作,SWD自然连不上。
第二步,检查SWDIO和SWCLK接线。这两个脚最容易接反。接反以后SCK还能进,但数据线完全对不上,报错最常见的就是unknown device id。我见过太多案例,排查到最后发现是杜邦线插反了。SWDIO接PA13,SWCLK接PA14,GND必须共地,这个顺序错一个都不行。
第三步,把SWD频率降下来。如果你的SWD线比较长,或者用了面包板飞线,信号完整性会出问题。Keil里打开Options for Target→Debug→Settings,把SWD频率从默认的4MHz往下降到1MHz甚至100kHz。调试环境差的时候,降低频率解决90%以上的连接问题,这是ST官方FAE都推荐的做法。
第四步,检查芯片是否进入读保护。如果之前烧录过开启RDP Level 1的固件,芯片的SWD端口就只能做全片擦除,不能连接调试。这时候报的也是unknown device id。解决办法是用ST-Link Utility执行Full Flash Erase,或者用CubeProgrammer连接时选择Hot Plug模式,把Level 1降回Level 0。查这个很快:如果芯片连XTAL都没焊或者晶振没起振,ST-Link也能连上SWD,因为SWD不依赖外部时钟。所以连不上还真别赖晶振。
4.3 设备管理器出现未知设备的驱动修复
如果第一步就发现设备管理器里是未知设备,按这个套路来:
- 右键未知设备→更新驱动→浏览计算机以查找驱动→从计算机的设备驱动列表中选择。
- 如果列表里没有ST-Link相关驱动,去ST官网下载最新的ST-Link驱动安装包。
- 安装后拔掉ST-Link重新插,看设备管理器是否变成正常设备。
- 变正常后,打开STM32 ST-LINK Utility验证连接,能识别到目标芯片就说明初始问题已解决。
这里要特别提醒一点:山寨ST-Link千万别手贱去点固件升级。ST官方工具在升级时会先擦除旧固件再写新固件,山寨板子的Flash型号和原版不同,升级到一半就会直接变砖。变砖后设备管理器里还显示VID_0483,但USB枚举出来的设备描述符字段是残缺的,PID变得很奇怪,比如PID_0000或者PID_FFFF。遇到这种PID直接死心,要么换一个烧录器,要么找能重刷固件的工具。我工作室抽屉里就有三块这么废掉的山寨ST-Link,都是当时不信邪试出来的教训。
山寨识别的方法也不复杂:正品ST-Link/V2外壳上有ST标志,PCB颜色和字迹清晰,固件版本可以用官方工具读出来和最新版对比;山寨的固件版本要么显示一个奇怪数字,要么升级直接报错。更靠谱的方法是看USB枚举信息里的iSerialNumber,正品序列号是唯一的,山寨经常所有设备都是同一个序列号。
4.4 用VID/PID反向验证ST-Link的身份
拿到一块来路不明的ST-Link,最快验证方法就是查设备管理器里的VID/PID,再和官方值对比。
| 设备类型 | 正常VID:PID | 异常表现 |
|---|---|---|
| ST-Link/V2 | 0483:3748 | 固件升级后变砖设备ID为0483:0000 |
| ST-Link/V2-1 | 0483:374B | 山寨可能报其他PID |
| ST-Link/V3 | 0483:374D | 山寨较少,但要注意序列号异常 |
不过你也会遇到很阴间的山寨货,固件克隆得足够好,VID/PID/序列号和正品一模一样。所以设备管理器里的ID只能作为“必要条件”,不能作为“充分条件”。遇到排查过所有可能还连不上的情况,换个正品烧录器交叉测试是最快的鉴别手段。
这里我还想分享一个真实案例:以前一个客户拿了一块自称“ST原厂”的板子来修,现象是ST-Link报unknown device id。我查设备管理器,VID_0483正常,PID_3748正常,驱动也正常。然后我把ST-Link单独接目标板测量,发现SWCLK引脚电压只有0.8V,正常应该被上拉到3.3V。拿放大镜一看,芯片SWCLK引脚虚焊。补焊后一切正常。当时客户已经准备返厂换新板子了,结果是一个焊点的问题。硬件调试就是这样,基础检查做得越认真,越能避免冤枉人。
5. 常见问题速查表与独家避坑技巧
5.1 故障速查表:从现象到解决一步到位
我把实际操作中遇到过的典型问题整理成一个速查表,遇到类似情况直接对照着排查,能省不少时间:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 设备管理器里插上后无任何反应 | USB线只供电不传数据 | 换一条好的USB线试试 |
| 设备枚举成未知设备,硬件ID为USB\UNKNOWN | 设备USB时钟异常或拉低D+/D- | 检查晶振电路,量USB_D+/D-波形 |
| 未知设备,硬件ID为VID_0483&PID_3748 | 驱动未安装或驱动损坏 | 重装ST-Link驱动 |
| ST-Link能识别但连不上芯片 | SWD接线错、目标板供电不足、芯片锁死 | 按4.2节顺序操作 |
| 连上芯片但读ID读出0xFFFFFFFF | SWDIO/SWCLK接触不良或信号质量差 | 换短线、降频率、检查排针焊接 |
| 目标板自己供电时能连,只靠ST-Link供电连不上 | 目标板耗电过大 | 单独给目标板供3.3V且共地 |
| 电脑识别正常但Keil弹“RDDI-DAP Error” | SWD线过长或干扰 | 降低SWD频率,缩短杜邦线长度 |
5.2 避开驱动安装的三个大坑
驱动这一关卡住了无数新手,我见过的坑大概就这三类:
第一个坑是Windows驱动签名强制。Win10/11默认不允许安装未签名驱动,ST官方驱动是签名的没问题,但一些老版本驱动、第三方驱动会装上就失败。解决办法是开机时按F8进入高级启动选项,选择“禁用驱动程序强制签名”,或者用测试模式。注意这个设置重启后不会持久,每次重启都要重新来一次,别问我怎么知道的。
第二个坑是安装驱动时USB设备已经插在电脑上。Windows有时会先自动安装一个错误驱动,导致你手动安装时被“已安装的最佳驱动”挡住。解决方法是拔掉设备,卸载设备管理器里残留的设备节点,然后不插设备装驱动,装完再插入设备。
第三个坑是旧驱动残留冲突。比如你之前装过另外一款ST-Link工具附带的老驱动,新驱动装上去被“占位”了。解决方法是完全卸载旧工具包,然后用驱动清理工具把设备管理器里的隐藏设备残骸清理干净,再重装驱动。
5.3 给USB设备开发者的额外补丁
如果你在做自己的USB设备,以下几个经验可能让你少走几个月弯路:
- 描述符里的字符串别省略。很多开发者图省事把
iManufacturer和iProduct设为0,设备管理器里显示“未知设备”,这对用户的信任度影响非常大。至少在产品阶段要写上厂商名和产品名。 - 量产时PID按型号规划。比如你未来会有三款产品,PID分别规划成0x0001、0x0002、0x0003,同类产品的不同硬件版本用REV字段区分。这样以后写上位机时按PID分型号,按REV分版本,逻辑会非常清爽。
- 不要把VID/PID留在安卓或Linux内核的驱动列表里当“免驱桌面”。一些宽泛匹配的内核驱动看到VID符合就绑定,导致你的设备被内核认领,用户态程序读不到。遇到这种问题,需要写udev规则排除或者驱动绑定优先级。
- 调试阶段可以统一用一个测试VID/PID,然后给自己写一个专门的上位机过滤逻辑,这样开发板和应用软件解耦。但发布时一定要改成正式ID,否则会出现“全世界都在用同一个PID”的尴尬情况。
最后再分享一个小技巧:平时维修和调试,可以维护一个自己的VID/PID速查表,把常见芯片、常见开发板、常见烧录器的ID整理成Markdown或Excel,遇到陌生设备先查表再上网搜,排查速度会快很多。我这几年整理的表里已经存了上百条记录,每次遇到不认识的设备,查表加搜索基本十分钟内能定位到具体芯片方案。
做硬件这行,很多看似玄学的故障,最后查出来都是特别基础的问题。就像ST-Link报unknown device id,折腾半天换了好几根线,最后发现只是SWDIO接错了一个脚。有了VID/PID这套识别体系,你能很快把问题范围从“整机故障”缩小到“USB链路”“驱动层”还是“目标侧”,剩下的就只是按顺序做排除法了。