【学习记录】USB连接与枚举全解析:从D+/D-上拉到pyOCD烧录
在嵌入式开发中,USB是最常用的通信接口之一。但很多工程师只知道"插上USB线就能用",却不清楚主机是如何识别设备、判断速度、完成枚举的。本文从D+/D-上拉电阻讲起,完整拆解USB速度检测、chirp握手、枚举流程,并结合CMSIS-DAP调试器和pyOCD烧录场景,给出实战排查指南。
📌 目录
- 一句话结论(#一 一句话结论)
- USB速度等级与D+/D-的决定作用
- 高速设备握手过程
- 电脑枚举流程
- Windows下查看速度和枚举状态
- [CMSIS-DAP / pyOCD场景补充](#CMSIS-DAP / pyOCD场景补充)
- 常见故障与排查
- 总结
一、一句话结论
USB连接后,主机先靠设备端的D+/D-上拉电阻判断"低速还是全速";如果是高速设备,再通过chirp握手切到高速。枚举则是主机通过控制端点0读取描述符、分配地址、加载驱动,最终让设备可用。
二、USB速度等级与D+/D-的决定作用
USB 2.0有三种速度:
| 速度 | 速率 | 设备端上拉 | 典型设备 |
|---|---|---|---|
| 低速 Low Speed | 1.5 Mbps | D- 上拉 1.5kΩ | 键盘、鼠标 |
| 全速 Full Speed | 12 Mbps | D+ 上拉 1.5kΩ | CMSIS-DAP、HID、音频 |
| 高速 High Speed | 480 Mbps | 先 D+ 上拉,握手后切高速 | U盘、高速采集卡 |
主机端D+/D-各有15kΩ下拉。设备插入后:
- 若主机检测到 D-被拉高 → 低速设备;
- 若检测到 D+被拉高 → 全速设备;
- 若D+上拉后,主机复位期间发chirp,设备回应 → 切到高速;
- 主机不支持高速,或设备不支持,就保持全速。
⚠️ 注意:高速设备不是靠D+/D-直接决定,而是"先以全速身份出现,再握手升级"。USB 3.0有独立TX/RX引脚,不靠D+/D-判断超速。
硬件原理图检查要点:
- D+上拉1.5kΩ(全速)或D-上拉1.5kΩ(低速)
- 主机端15kΩ下拉(通常在Hub或主控内部)
- 上拉电阻必须连接到3.3V(不能接5V,否则可能损坏芯片)
三、高速设备握手过程
高速设备的握手流程如下:
- 设备插入,D+上拉,主机识别为全速。
- 主机发总线复位(SE0)。
- 主机发chirp K。
- 设备回chirp K/J序列。
- 双方切换高速终端,D+上拉断开。
- 之后进入高速通信。
主机 USB设备 主机 USB设备 #mermaid-svg-QYccyq6vtkbeACJa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QYccyq6vtkbeACJa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QYccyq6vtkbeACJa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QYccyq6vtkbeACJa .error-icon{fill:#552222;}#mermaid-svg-QYccyq6vtkbeACJa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QYccyq6vtkbeACJa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QYccyq6vtkbeACJa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QYccyq6vtkbeACJa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QYccyq6vtkbeACJa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QYccyq6vtkbeACJa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QYccyq6vtkbeACJa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QYccyq6vtkbeACJa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QYccyq6vtkbeACJa .marker.cross{stroke:#333333;}#mermaid-svg-QYccyq6vtkbeACJa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QYccyq6vtkbeACJa p{margin:0;}#mermaid-svg-QYccyq6vtkbeACJa .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QYccyq6vtkbeACJa text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-QYccyq6vtkbeACJa .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-QYccyq6vtkbeACJa .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-QYccyq6vtkbeACJa .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-QYccyq6vtkbeACJa .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-QYccyq6vtkbeACJa #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-QYccyq6vtkbeACJa .sequenceNumber{fill:white;}#mermaid-svg-QYccyq6vtkbeACJa #sequencenumber{fill:#333;}#mermaid-svg-QYccyq6vtkbeACJa #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-QYccyq6vtkbeACJa .messageText{fill:#333;stroke:none;}#mermaid-svg-QYccyq6vtkbeACJa .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QYccyq6vtkbeACJa .labelText,#mermaid-svg-QYccyq6vtkbeACJa .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-QYccyq6vtkbeACJa .loopText,#mermaid-svg-QYccyq6vtkbeACJa .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-QYccyq6vtkbeACJa .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-QYccyq6vtkbeACJa .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-QYccyq6vtkbeACJa .noteText,#mermaid-svg-QYccyq6vtkbeACJa .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-QYccyq6vtkbeACJa .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QYccyq6vtkbeACJa .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QYccyq6vtkbeACJa .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QYccyq6vtkbeACJa .actorPopupMenu{position:absolute;}#mermaid-svg-QYccyq6vtkbeACJa .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-QYccyq6vtkbeACJa .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QYccyq6vtkbeACJa .actor-man circle,#mermaid-svg-QYccyq6vtkbeACJa line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-QYccyq6vtkbeACJa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 双方切换高速终端 进入高速通信 D+上拉(识别为全速) 总线复位(SE0) chirp K chirp K/J序列 断开D+上拉
如果这步失败,设备就停留在全速。
💡 实战提示 :CMSIS-DAP调试器绝大多数是全速12Mbps,足够烧录和调试。 不要盲目追求高速,全速对调试器来说完全够用。
四、电脑枚举流程
枚举就是主机"问清楚你是谁、怎么用你"的过程:
| 步骤 | 动作 | 说明 |
|---|---|---|
| 1 | 检测连接 | 主机看到D+/D-上拉,确认有设备 |
| 2 | 端口供电 | 给VBUS 5V,等待稳定 |
| 3 | 总线复位 | 设备进入默认状态,地址0,端点0 |
| 4 | 读设备描述符前8字节 | 获取 bMaxPacketSize0 |
| 5 | 分配地址 | SET_ADDRESS,设备获得唯一地址 |
| 6 | 读完整设备描述符 | 18字节,含VID、PID、类代码 |
| 7 | 读配置描述符 | 接口、端点、供电方式 |
| 8 | 读字符串描述符 | 厂商、产品、序列号 |
| 9 | 选择配置 | SET_CONFIGURATION,设备进入工作状态 |
| 10 | 加载驱动 | Windows根据VID/PID/类匹配HID、WinUSB、CDC等 |
| 11 | 设备就绪 | 应用层可通信 |
枚举失败常见提示:
- "未知USB设备(设备描述符请求失败)"
- "无法识别的USB设备"
- 设备反复插拔声
💡 枚举的本质 :主机通过控制端点0(Endpoint 0)与设备通信,读取各种描述符,最终确定设备的身份和使用方式。
五、Windows下查看速度和枚举状态
| 方法 | 作用 |
|---|---|
| USB Device Tree Viewer(UsbTreeView) | 最直观,显示Low/Full/High Speed、VID/PID、描述符 |
| 设备管理器 | 看是否识别、有无黄色感叹号 |
| USBView | 微软官方工具,查看USB拓扑和描述符 |
| Wireshark + USBPcap | 抓枚举包,分析控制传输 |
| 示波器/逻辑分析仪 | 测D+/D-静态电平:全速D+≈3.3V,D-≈0V;低速相反 |
Linux下:
bash
lsusb -t # 显示 480M/12M/1.5M
dmesg | grep usb
示波器测量方法:
- 全速设备:D+约3.3V,D-约0V
- 低速设备:D-约3.3V,D+约0V
- 高速设备:静态时两者都接近0V(因为上拉已断开)
六、CMSIS-DAP / pyOCD场景补充
- CMSIS-DAP调试器通常是全速USB HID或WinUSB设备。
python -m pyocd list能看到调试器,前提是USB枚举成功且驱动匹配。- 如果
pyocd list为空,先看设备管理器:
| 设备管理器状态 | 可能原因 | 排查方向 |
|---|---|---|
| 有"未知USB设备" | 枚举失败 | 查D+上拉、晶振、USB走线、供电 |
| 有HID/WinUSB设备但pyOCD不识别 | 驱动或VID/PID问题 | 查VID/PID、CMSIS-DAP固件、驱动 |
⚠️ 芯片限制 :STM32F103只有全速USB,不能跑高速;STM32F4的OTG_HS需外接ULPI PHY才能480Mbps。
pyOCD调试流程:
USB枚举成功 → 驱动匹配 → pyOCD识别设备 → 烧录/调试
任何一环失败,pyOCD都会显示"找不到设备"。
七、常见故障与排查
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 电脑无反应 | D+无上拉、VBUS未到、USB线只供电 | 测D+电压、换线 |
| 低速被识别 | D-上拉,或D+/D-接反 | 查原理图、测上拉位置 |
| 未知设备 | 枚举失败、描述符错误、时钟不准 | UsbTreeView看描述符、查晶振 |
| 反复重连 | 供电不足、看门狗复位、中断阻塞 | 测5V/3.3V、查固件 |
| pyOCD找不到 | 驱动未装、VID/PID不对 | 设备管理器、换HID/WinUSB模式 |
排查思路总结:
- 先看硬件:D+/D-上拉是否正确,VBUS是否到位,晶振是否起振。
- 再看枚举:用UsbTreeView确认是否识别为全速设备,描述符是否正常。
- 最后看驱动:设备管理器确认驱动是否加载,pyOCD是否匹配。
八、总结
| 阶段 | 核心动作 | 关键点 |
|---|---|---|
| 速度检测 | D+/D-上拉决定低速/全速 | 全速D+上拉1.5kΩ,低速D-上拉1.5kΩ |
| 高速握手 | chirp K/J序列 | 失败则停留在全速 |
| 枚举 | 读描述符、分配地址、加载驱动 | 控制端点0是关键通道 |
| 调试器 | CMSIS-DAP通常是全速设备 | 枚举成功+驱动匹配=pyOCD可用 |
一句话总结:D+/D-上拉决定低速/全速,高速靠chirp握手;电脑枚举就是读描述符、分配地址、装驱动。调试器枚举成功,pyOCD才能烧录。