1. 硬件标识符的基石为什么VID/PID不是一串随机数字在嵌入式硬件开发尤其是涉及USB通信的设备开发中新手最容易犯的一个错误就是随便找一组十六进制数字比如0x1234和0x5678就当作自己产品的VIDVendor ID厂商ID和PIDProduct ID产品ID写进代码里。这看起来省事但实际上是在给自己的产品埋下巨大的兼容性“地雷”。为什么不能这么做这得从整个计算机硬件生态的底层逻辑说起。想象一下你开发了一块基于RP2040的键盘我开发了一块基于ESP32-S3的HID设备我们都“随机”选择了0xDEAD和0xBEEF作为VID/PID。当用户同时将这两款设备插入电脑时操作系统会彻底懵掉——它看到两个完全不同的硬件一个是键盘一个是其他HID设备却拥有完全相同的“身份证号”。结果可能是驱动装错、功能紊乱甚至直接导致系统蓝屏。VID/PID这套机制就是为了从根本上杜绝这种混乱。它的核心价值在于全局唯一性与权威分配。VID由USB-IFUSB Implementers Forum统一管理和销售一个VID代表一个合法的硬件厂商。PID则由拥有VID的厂商自行管理确保在自己产品线内唯一。这套组合VID:PID在全球范围内构成了硬件的唯一身份标识。这不仅仅是USB协议的要求更是现代操作系统进行设备识别、驱动匹配、权限管理的基石。从你插上设备的那一刻起操作系统就在后台进行着一系列精密操作它读取设备描述符中的VID/PID在庞大的驱动数据库中寻找匹配项为设备加载正确的驱动程序分配系统资源如端点、中断并在用户层面呈现为一个可用的硬件。如果你的标识符是“黑户”这一切流程都可能出错。更具体地说一个合法的VID/PID直接影响以下几个方面固件更新DFU/UF2像CircuitPython、Arduino IDE或芯片厂商的烧录工具都依赖VID/PID来识别目标设备。错误的ID会导致工具无法识别你的板子或者更糟把固件刷到别人的设备上。操作系统驱动匹配Windows、macOS、Linux通过udev规则都使用VID/PID作为加载.inf、.kext或规则文件的关键索引。ID冲突意味着你的设备可能永远用不上正确的驱动。设备枚举与用户权限在ChromeOS或配置了严格udev规则的Linux系统上设备访问权限如串口读写、HID访问是基于VID/PID锁定的。一个非法的ID可能导致设备根本无法被用户空间的程序访问。开发环境集成Arduino IDE、PlatformIO等开发平台利用VID/PID来自动识别开发板型号从而预配置正确的编译选项、核心库和烧录工具链。所以为你的硬件获取一个合法的VID/PID不是可选项而是产品能够可靠融入现有生态系统的准入门票。接下来我们就深入拆解获取这张“门票”的几种主流路径。2. 获取合法VID/PID的三条核心路径解析面对“如何获取VID/PID”这个问题开发者通常有三条路可走。选择哪一条取决于你的公司性质、产品采用的芯片以及产品的开源程度。下面这张表格清晰地对比了这三种路径的核心区别路径适用对象成本唯一性保证管理方典型流程向USB-IF购买VID商业公司、计划量产销售产品的团队较高一次性费用约$6000全球最高商业权威USB-IF1. 在USB-IF官网注册账户并提交申请。2. 支付费用。3. 获得一个专属的VID16位十六进制数。4. 自行管理分配PID16位十六进制数。遵循芯片原厂流程使用特定厂商如Espressif、Raspberry Pi、MicrochipMCU的开发者通常免费或极低成本在原厂生态内唯一受原厂支持芯片制造商1. 联系芯片原厂技术支持或查阅开发者门户。2. 提交产品信息申请。3. 获得原厂分配或授权使用的VID/PID对。通过pid.codes申请符合开源硬件定义的项目硬件设计完全开源免费在开源硬件社区内唯一具有公信力pid.codes社区1. 确保硬件项目符合OSHW开源硬件定义。2. 在GitHub上fork并编辑pid.codes数据库文件。3. 提交Pull Request附上硬件开源仓库链接。4. 社区审核通过后合并分配PID。2.1 路径一向USB-IF购买——商业产品的标准答案如果你所在的公司计划将产品商业化量产并销售那么直接向USB-IF购买一个属于自己的VID是最规范、最一劳永逸的选择。操作流程与考量你需要访问USB-IF官方网站找到Vendor ID申请页面。整个过程更像是一次商业采购填写公司信息、签署协议、支付费用目前约6000美元一次性买断。成功后你会获得一个全球唯一的16位VID例如Adafruit的是0x239A。此后你公司旗下的所有USB产品其PID都可以由你自行定义和管理只需确保在公司内部产品线中唯一即可。注意这笔费用对于初创团队或个人开发者可能是一笔不小的开支。因此除非产品已明确走向大规模商业销售否则初期可以考虑其他路径。另外购买VID后你有责任妥善管理自己的PID分配建议建立内部登记制度避免未来产品间的PID冲突。2.2 路径二利用芯片原厂的分配机制——性价比之选许多微控制器MCU制造商为了降低开发者门槛会为其芯片提供“公版”或可申请使用的VID/PID。这对于采用特定平台进行开发的团队来说是极具性价比的方案。Espressif乐鑫对于ESP32-S2、ESP32-S3、ESP32-C3等支持USB OTG的芯片乐鑫通常会提供一个可供开发者使用的VID/PID例如ESP32-S2在Arduino环境下常用0x303A作为VID。你需要查阅乐鑫的官方技术文档或通过其开发者论坛确认当前可用的ID及使用条款。Raspberry Pi树莓派树莓派基金会为其RP2040微控制器芯片的客户提供了PID分配服务。如果你使用RP2040设计产品可以向树莓派基金会申请一个属于你产品的PID并与RP2040的公共VID配合使用。Microchip微芯作为老牌MCU厂商Microchip也有类似的计划。通常你需要通过其销售或技术支持渠道进行申请。实操心得使用原厂提供的ID时务必仔细阅读相关许可协议。这些ID通常仅允许用于基于该厂商芯片的产品且不能用于直接克隆或与官方开发板竞争的产品。它的优势是零成本或低成本并且能获得原厂一定程度的技术背书。劣势是ID与原厂绑定如果你的产品未来要更换主控芯片这个ID可能就无法继续使用了。2.3 路径三拥抱开源——pid.codes社区方案对于完全开源遵循OSHW定义的硬件项目pid.codes提供了一个绝佳的免费解决方案。这是一个由社区维护的开放式数据库专门为开源硬件项目分配PID。申请步骤详解准备开源仓库确保你的硬件项目所有设计文件原理图、PCB布局、BOM、固件源码均已在一个公开的代码仓库如GitHub、GitLab中开源并使用了合适的开源硬件许可证。Fork与编辑访问pid.codes的GitHub仓库Fork到你的账户。数据库通常是一个结构化的文本文件如boards.txt或pids.yml。你需要按照既定格式在其中添加你的产品信息包括申请到的VID对于开源项目常使用社区共享的VID如0x1209、请求的PID、产品名称、项目仓库链接等。提交Pull Request (PR)提交PR并清晰描述你的硬件项目。社区维护者会审核项目的开源合规性。审核与合并审核通过后你的PR会被合并分配到的PID将正式生效。你可以在产品的文档和固件中使用这个VID:PID对。提示pid.codes的审核核心是项目的开源完整性。确保你的仓库不是空壳必须包含可生产/可复现的所有必要文件。这是一个既获得合法ID又能为开源社区做贡献的双赢方式。3. 无原生USB设备的特殊标识Creation ID你的项目正文里提到了一个关键例外“如果板子不使用原生USB”。这是什么情况典型例子就是早期的ESP32非S2/S3系列或一些仅通过UART串口进行通信的简单设备。这些设备没有USB控制器因此不存在标准的USB描述符自然也就没有VID/PID。但是像CircuitPython这样的框架即使在通过串口连接时也需要一个唯一标识来区分不同的板型以加载正确的固件包或识别板载功能。这时就需要用到“Creation ID”。Creation ID是什么它是一个128位的UUID通用唯一识别码同样要求全球唯一。它不遵循USB-IF的体系而是为那些没有USB接口的硬件设备提供的一个替代性唯一标识方案。如何获取Creation ID对于开源硬件项目最常用的途径是通过creationid在GitHub上的仓库来申请。流程与pid.codes类似确保你的硬件项目完全开源。在creationid仓库中提交一个Issue或PR按照模板提供你的硬件信息。社区维护者会为你生成一个唯一的UUID。在固件中你需要将这个Creation ID以特定格式例如存储在微控制器的特定Flash地址或作为常量定义在代码中提供给CircuitPython运行时以便它能够正确识别你的硬件。注意事项对于同时具有USB和UART模式的设备很多开发板都是如此最佳实践是同时拥有VID/PID和Creation ID。USB模式时使用前者纯串口模式时使用后者确保设备在任何连接方式下都能被唯一、正确地识别。4. 在项目中集成与使用VID/PID的实操指南获取到合法的VID/PID后下一步就是将其集成到你的硬件项目中。这涉及到硬件描述符定义和软件工具链配置两个层面。4.1 在固件代码中定义VID/PID无论你使用Arduino框架、CircuitPython还是裸机USB库都需要在源代码中指定VID和PID。以下是一个Arduino框架基于ESP32-S3的示例// 在项目的platformio.ini或boards.txt中定义是更规范的做法 // 但也可以在代码中直接设置USB描述符如果库支持 // 对于ESP32-S3使用Arduino框架通常需要在“工具”菜单选择开发板型号其背后已配置了VID/PID // 如果你想自定义可能需要修改开发板支持包中的定义文件 // 一个更通用的示例概念性代码 void setup() { // 对于像TinyUSB这样的库你可能会这样初始化 // TinyUSBDevice.setID(VID, PID); // VID和PID是你的16位十六进制数 // TinyUSBDevice.begin(); } void loop() { // ... }更常见的做法是修改开发板定义文件。例如在Arduino的硬件包中每个开发板都有一个boards.txt文件其中包含类似这样的行your_board.vid.00x239A your_board.pid.00xXXXX对于CircuitPythonVID/PID通常定义在移植port层的mpconfigboard.h或mpconfigboard.mk文件中// 在 mpconfigboard.h 中 #define USB_VID 0x239A #define USB_PID 0xXXXX #define USB_PRODUCT Your Awesome Board关键步骤确定定义位置查阅你所用的开发框架文档找到正确定义USB VID/PID的位置。使用十六进制格式确保以0x开头的十六进制格式写入。同时定义产品字符串USB_PRODUCT或类似定义应与你的产品名一致这会在操作系统的设备管理器中显示方便用户识别。4.2 配置构建系统与烧录工具VID/PID不仅用于运行时识别也用于烧录和调试阶段。PlatformIO在platformio.ini中你可以通过board_build.usb_product和自定义的构建脚本或修改开发板定义来设置。Arduino IDE如前所述主要通过选择或自定义开发板定义来实现。UF2 Bootloader如果你的设备支持UF2拖放烧录VID/PID需要被编译进bootloader。这样当设备进入UF2模式时计算机会将其识别为一个特定的USB大容量存储设备。Adafruit的UF2 bootloader就严重依赖VID/PID来区分不同的板型。实操现场记录我曾为一个使用RP2040的自制键盘项目申请了pid.codes的PID。在集成时我不仅修改了CircuitPython固件中的mpconfigboard.h还需要修改用于构建UF2 bootloader的pico_sdk_import.cmake相关配置确保在bootloader模式和正常运行模式下计算机看到的是两个不同的、但都合法的PID例如一个用于UF2磁盘0x239A:0x00XX一个用于CircuitPython串口0x239A:0x00YY。这完美解决了双模式设备的识别问题。4.3 操作系统端的适配以Linux udev为例为了让你的设备在Linux系统上获得正确的权限例如非root用户可以直接访问串口你需要编写udev规则。udev规则的核心匹配条件就是ATTRS{idVendor}和ATTRS{idProduct}这正是你的VID和PID。示例udev规则文件 (/etc/udev/rules.d/99-my-board.rules)# 为特定VID/PID的设备设置串口读写权限 SUBSYSTEMtty, ATTRS{idVendor}239a, ATTRS{idProduct}00xx, MODE0666, GROUPdialout # 为UF2 bootloader模式设置挂载权限如果需要 SUBSYSTEMblock, ATTRS{idVendor}239a, ATTRS{idProduct}00yy, MODE0666编写好规则后运行sudo udevadm control --reload-rules sudo udevadm trigger使其生效。这样当你的设备插入时Linux会自动应用这些权限设置。5. 常见问题、排查技巧与避坑指南在实际开发中围绕VID/PID的问题层出不穷。下面我整理了一份从问题现象到排查思路的速查表并附上一些血泪教训换来的技巧。问题现象可能原因排查思路与解决方案电脑完全无法识别设备设备管理器出现“未知USB设备”1. VID/PID在系统中无对应驱动。2. 固件中USB描述符配置错误如端点、接口描述符。3. 硬件USB电路问题DP/DM接反、终端电阻缺失。1.首要检查确认VID/PID是否已在代码中正确定义并编译进去。使用lsusb(Linux/macOS) 或USB设备树查看工具(Windows)确认系统是否读到预期ID。2. 如果ID正确但仍无法识别问题可能超出VID/PID范围需检查USB堆栈配置和硬件。设备被识别为其他产品如显示为“Arduino Leonardo”固件中使用的VID/PID与其他常见开发板如Arduino官方板冲突。1.立即更换你使用的ID很可能是“借用”了别人的。必须更换为你自己合法申请的ID。2.检查冲突在lsusb输出中搜索你的ID看是否与其他已知设备重复。UF2模式与正常运行模式ID相同导致冲突没有为设备的两种不同USB模式Bootloader/运行时分配不同的PID。这是必须遵守的规则。为bootloader和主固件分配两个连续的或相关的PID。例如主模式用0x00ABUF2模式用0x00AC。在bootloader和主固件的代码中分别设置。Linux下需要sudo才能访问串口缺少对应VID/PID的udev规则。如4.3节所述编写并安装正确的udev规则匹配你的设备VID/PID。CircuitPython/Arduino IDE无法自动识别板型板型定义文件如Arduino的boards.txtCircuitPython的board_id中未包含你的新VID/PID。1. 对于开源项目可以向上游Arduino核心库、CircuitPython提交板型支持PR。2. 临时方案在IDE中手动选择端口或使用通用板型配置功能可能受限。pid.codes或creationid的PR被拒绝1. 项目开源不完整缺少原理图、PCB文件。2. 申请的ID格式错误或与现有冲突。3. 项目描述不清。1.仔细阅读提交指南确保仓库包含所有必需的开源文件。2.在提交前本地验证使用项目提供的脚本检查数据格式。3.清晰沟通在PR描述中详细说明硬件功能和开源链接。独家避坑技巧“借用的ID”是定时炸弹千万不要使用知名开发板如Arduino Uno的0x2341:0x0043或芯片评估板的ID。即使暂时能用一旦用户同时连接你的设备和原版设备灾难就会发生。这是产品级开发的大忌。提前规划PID段如果你拥有自己的VID建议提前规划PID分配方案。例如0x0001-0x00FF留给开发板和内部测试0x0100-0x01FF留给A系列产品0x0200-0x02FF留给B系列产品。建立内部文档记录分配情况。双模式设备的PID管理对于有bootloader的设备强烈建议采用“主PID1”的策略给bootloader用。这样既有关联性又避免了冲突。在代码中可以用宏定义来管理#define USB_VID 0x239A #ifdef BOOTLOADER_MODE #define USB_PID 0x00AB #define USB_PRODUCT MyBoard Bootloader #else #define USB_PID 0x00AC #define USB_PRODUCT MyBoard #endif测试阶段的“安全ID”在早期硬件调试阶段固件可能不稳定。可以使用一些公认的“测试用”PID范围需查证当前约定例如某些社区保留的0xFF00-0xFFFF范围但一旦进入原型验证阶段就应切换为正式申请的ID。验证工具链在发布固件前使用usbhid-dump、sigrok的USB协议分析功能或者简单的dmesg | grep usbLinux来查看设备插入时系统日志中的VID/PID信息确保与预期完全一致。硬件标识符是连接物理设备与数字世界的桥梁VID/PID则是这座桥上最关键的编号。投入时间理解其机制并合规地获取、使用它们看似是前期的一项繁琐工作实则是为你产品的长期稳定性、兼容性和用户体验打下最坚实的基础。在开源硬件社区使用pid.codes的ID更是一种彰显项目合规性与专业性的方式。我自己的项目从最初随意填写的ID到后来因为冲突问题焦头烂额再到最终通过pid.codes申请到合法ID整个过程让我深刻体会到遵循规范虽然多花了一点时间但避免了无数潜在的售后支持和兼容性骂名这笔投资回报率极高。