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

资讯详情

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

MCU复位后USB不重新枚举?从DP上拉波形到软断开全解析

MCU复位后USB不重新枚举?从DP上拉波形到软断开全解析 有一段时间我一直被一个不起眼但特别磨人的问题折腾用LAT1689做主控的USB设备只要一按复位键PC端就不提示设备断开。设备管理器里那个USB设备图标还亮着甚至状态显示“已连接”但应用层的数据收发已经完全没反应。必须把USB线拔掉再重新插回去设备才恢复正常。一开始我以为是Windows驱动缓存的问题结果用逻辑分析仪抓了DP/DM波形之后发现问题出在设备端的总线状态上。这件事看起来简单但涉及USB枚举机制、MCU复位行为、电源时序、上拉电阻控制等多个层面。如果你也在做USB设备或者正在为“MCU复位后USB不重枚举”这类问题犯愁这篇实战记录应该能帮你少走不少弯路。我会从现象确认、底层原因、排查工具、解决方案到避坑清单完整过一遍。1. 问题背景与现象确认1.1 故障现场还原我手头这块板子是基于LAT1689的自定义HID设备通过USB枚举后能够正常收发数据。复位按键直接接在NRST引脚上按下后MCU重启、固件重新运行但PC端不会出现常见的“USB设备已断开/已连接”提示音。设备管理器里设备节点一直挂在那里PID/VID也没有变化可实际发送数据时应用层直接超时。这个现象不是偶发基本每次手动复位都会复现。如果先关闭PC端软件再拔插USB线设备就能重新正常枚举。也就是说只有“MCU复位”这个动作会让PC端认为连接还活着而物理拔插则能强制触发重新枚举。这已经足够说明问题不在应用层也不在驱动而在MCU复位前后的USB物理链路状态。1.2 为什么“未断开”不是小问题这种问题在开发调试时容易被忽略因为“重新拔插一次就好了”。但放到量产和测试阶段就非常头疼自动化测试脚本会通过USB发送指令如果PC端认为设备还连着脚本不会等重新枚举后续指令全部失败误报率极高。OTA升级后如果MCU自动复位设备管理器里旧设备节点不消失上位机判断设备ID时会直接认错。在某些USB Hub级联场景下一个设备长时间“假连接”会影响整个端口带宽严重时Hub甚至会拒绝后续设备的接入。所以这个问题本质上不是“提示音”的问题而是主机和设备的连接状态不一致直接影响业务可靠性和测试效率。1.3 先定义清楚问题边界排查之前要先想明白到底是谁认为连接没断开。PC端的“未断开”来自两个维度逻辑层设备管理器、应用层看到的枚举设备节点。物理层USB Hub端口上的D/D-电平状态以及Hub向Host上报的连接状态。如果物理层一直是“设备已连接”那么PC端不会主动去重新枚举逻辑层自然保持旧状态。如果物理层已经变成了断开状态但PC端还显示设备存在那才是系统缓存或驱动问题。我这次的故障更接近前者MCU复位后DP信号继续维持上拉主机根本感知不到断开所以一切“软件重新枚举”的办法都不会生效。2. USB连接保持的底层原因分析2.1 从USB枚举的基本逻辑说起USB是主从架构设备插入后Host通过检测D/D-的电平变化来识别设备是否接入。全速设备会在D上拉一个1.5kΩ电阻低速设备则在D-上拉Host根据哪根线被拉高判断设备速度等级。设备断开时上拉电阻消失d和d-都被拉低进入SE0状态。USB Hub检测到这个状态后才会向Host上报“设备断开”。换句话说只要DP在复位期间仍然保持高电平Host就会认为设备一直在线。USB协议没有“心跳”机制设备端如果只是“不响应”主机不会立刻判定断开大部分时候要等当前URB超时或者Hub重新检测总线状态。所以解决这个问题的关键就是恢复位时有没有让DP上拉可靠、稳定地消失一段时间。2.2 复位后LAT1689的设备端到底发生了什么MCU复位动作会重置CPU、总线和大部分外设但USB PHY不一定马上进入高阻状态。很多MCU的USB PHY是独立模拟电路复位时只要供电还在PHY的输出和内部上拉就不受CPU跑没跑起来的影响。尤其是那些把DP上拉电阻做在芯片内部的型号如果复位后的寄存器默认值是“上拉使能”那DP会继续保持高电平。LAT1689的具体上拉控制方式需要查数据手册但根据我当时的实测复位瞬间DP有一个很短的低脉冲随后又恢复为高。这个低脉冲持续时间只有几百微秒可能是复位导致的瞬间状态抖动但USB Hub对断开事件的检测需要一定时间去采样确认几百微秒往往不够。于是Host还没识别到断开DP又已经被MCU重新拉高了整个复位过程在Host看来就是一次“没有断开的短暂干扰”。2.3 不同复位类型的差异手动按键复位属于外部复位NRST被拉低整颗芯片进入复位状态寄存器恢复默认值。但芯片供电没有断USB PHY的供电轨还在所以PHY内部的状态不一定被彻底清掉。软件复位比如看门狗复位、NVIC_SystemReset对外设和引脚的影响更依赖芯片设计。有些芯片的软件复位不会重置所有域的电源USB控制器可能被复位但引脚复用功能和PHY模拟部分依然保持旧配置。这就会导致复位后DP上拉并没有被移除PC端自然认为设备还连着。还有一种情况更隐蔽固件在复位后启动速度特别快可能在PC端的USB Hub尚未完成断开检测之前就重新初始化USB并拉起了DP。这种情况下即使硬件本可以识别到断开也会被快速启动“掩盖”掉。所以不能只看复位动作本身还要看复位后的初始化时序。2.4 PC端和USB Hub的逻辑“粘滞”问题理论上USB Hub只要发现端口总线变成SE0就会向Host上报“设备断开”。但UB往往不是实时主动上报而是周期性扫描端口状态。扫描周期可能几毫秒到几十毫秒不等如果MCU复位后的DP重新拉高过程太短太快Hub在几个扫描周期内可能恰好没抓到低电平状态就一直停留在“已连接”。再加上Windows即插即用管理器对已经枚举的设备有缓存机制它不会因为一次总线状态异常就立刻销毁设备节点。只有当Host明确收到“断开”事件然后等待新的“连接”事件才会触发重新枚举。也就是说PC端“设备未断开”的现象既可能是设备端没有断开也可能是断开时间太短Host根本没机会上报。因此排查时必须从物理波形入手不能只看设备管理器。3. 排查步骤与实操3.1 第一步用逻辑分析仪抓复位瞬间的DP/DM电平遇到USB枚举类问题最有效的手段就是抓波形。不需要一开始就上昂贵的USB协议分析仪一台100MHz以上采样率的逻辑分析仪就够用。把通道1接D通道2接D-通道3接NRST三个通道同时采样按下复位按键记录完整的电平变化。我这边抓到的现象是NRST拉低后DP确实出现了短暂的低电平脉冲但很快恢复为高全程没有出现典型的断开状态。USB规范里全速设备断开时DP应该被拉低并保持一段时间让Hub能够采样确认。这个脉冲时间太短Hub没有上报断开才会出现PC端“未断开”的假象。3.2 第二步区分内部上拉和外部上拉如果你用的开发板上有外置1.5kΩ上拉电阻接到DP可以先飞线断开这个电阻只保留MCU内部上拉配置。然后再复位重复抓波形。如果断开外部上拉后DP在复位期间能够稳定拉低说明问题就在外部上拉电路上。如果DP还是迅速恢复高电平那就是芯片内部PHY在复位后主动使能了上拉。我遇到的情况比较典型外部上拉和内部上拉同时存在反而导致复位瞬间DP电平无法干净地掉下来。把外部上拉去掉之后问题减轻了很多但还需要配合代码里的软断开才能真正解决。所以这个测试很有必要能帮你快速定位到硬件还是软件。3.3 第三步检查复位引脚和电源时序复位引脚的波形也要一起看。如果NRST被拉低的时间不足MCU可能没有真正进入完全复位状态USB PHY可能只是被“毛刺”干扰了一下。可以用示波器同时测NRST、VDD、DP三条线确认按下复位按键时VDD有没有跌落以及NRST低电平时间是否满足数据手册要求。另一个容易忽略的点是复位按键和USB VBUS之间可能有耦合关系。比如PCB走线不小心靠近或者复位按键带了背光LED连接到VBUS按下复位键时LED瞬间消耗电流会导致VBUS发生微小跌落。如果这个跌落的幅度足够让USB PHY的部分电路触发异常就可能造成DP状态不稳定。不过这类问题比较少见一般出现在layout不够规范的小板子上。3.4 第四步从PC端工具反向验证在抓波形之前也可以先用软件工具辅助判断。插上设备等枚举成功然后打开USB Device Tree Viewer这类工具找到对应设备节点记录端口状态。按下复位键后立刻刷新工具视图如果端口状态从“Device attached”变成“No device connected”说明USB Hub已经识别到断开PC端还显示设备只是缓存问题。如果端口状态一直是“Device attached”并且DP波形也一直是高那基本可以锁定设备端没有真正断开。设备管理器也有参考意义但它更新不及时只能当作辅助判断。真正的依据还是端口状态和物理波形两者结合。3.5 第五步做最小复现实验排查到这一步建议把板子简化成“只供电、只连USB、只按复位键”的最小系统去掉所有外设、传感器和扩展接口。这么做的原因是LAT1689的某些GPIO在上电复位后默认状态不确定如果某个默认输出高的IO恰好和USB的DP/DM走线相邻或者直接复用成了DP就会干扰总线状态。我试过好几次问题在最小系统上依然复现才确定是MCU自身行为和USB上拉控制的问题。如果你在去掉外设后问题消失了那就要逐个接回外设找到哪个IO或模块在复位时改变了DP/DM电平。4. 解决方案与代码级修复4.1 方案一复位前主动断开USB软断开如果你的复位动作是软件可控的比如“收到上位机命令后复位”或者“OTA升级完成后复位”那最简单的办法就是在复位前显式关闭DP上拉让Host先识别到断开再执行系统复位。大多数USB控制器都有“Soft Disconnect”或类似功能通过寄存器关掉DP/DM上拉让总线进入SE0状态。以LAT1689为例代码思路大概是这样/* 复位前主动断开USB */ static void usb_prepare_reset(void) { // 关闭D上拉使总线进入SE0状态 USB_DEV_CTL ~(USB_DEV_DP_PULLUP_EN); // 保持一段时间确保PC端USB Hub完成断开检测 delay_ms(10); // 可选的打印日志或设置标志位 } void app_reboot(void) { usb_prepare_reset(); NVIC_SystemReset(); }注意一点延时不要太短。我测试下来10ms足够让绝大多数PC主控和Hub完成断开上报。如果你用的USB Hub比较老建议把延时加长到20ms效果更稳。软断开后MCU复位完成时DP处于低电平Host已经知道设备断了等固件重新初始化USB并拉起DP时Host就能重新枚举PC端也会正常提示“设备已连接”。4.2 方案二硬件上把DP上拉变成可控电源软件方案解决不了“外部按键复位”的场景因为按键按下时CPU是突然被复位的没有机会跑软断开代码。这时候就得用硬件兜底核心思路是让DP上拉电阻只在MCU正常运行时供电复位一发生就切断。我最终采用的电路是把DP的1.5kΩ上拉电阻接到一个MOS管开关上MOS管的栅极由MCU一个GPIO控制控制引脚上并联一个100kΩ下拉电阻。具体逻辑正常工作时GPIO输出高电平MOS管导通DP上拉电阻连接到3.3VUSB设备正常枚举。MCU复位瞬间所有GPIO回到默认状态控制引脚被外部100kΩ下拉电阻拉到低电平MOS管截止DP上拉被切断。DP失去上拉后自动拉低Host就识别到了“设备断开”。MCU复位完成、固件启动后再把控制引脚拉高DP上拉恢复USB重新枚举。这个方案的优点是不受复位源限制外部按键复位、看门狗复位、电源抖动导致的复位都能覆盖。唯一要注意的是GPIO复位后的默认状态必须是低电平或者高阻如果是高电平输出那复位期间MOS管依然导通方案失效。4.3 方案三修改USB初始化顺序强制“断开再连接”如果芯片没有外部上拉或者你已经完全依赖MCU内部DP上拉可以在USB初始化流程里做一次“强制断开”的时序控制。很多MCU的USB设备控制器里有一个Disconnect位或者类似功能初始化时需要先置位并等待一段维持时间再清位重新连接。代码示意/* 强制做一次USB断开/重连确保Host重新枚举 */ void usb_init_with_disconnect(void) { // 1. 使能USB模块时钟 RCC_USB_CLK_ENABLE(); // 2. 先进入强制断开状态 USB_DEV_CONFIG | USB_DEV_FORCE_DISCONNECT; // 3. 保持至少5ms确保Host看到断开 delay_ms(5); // 4. 初始化USB控制器和设备描述符 usb_core_init(); usb_device_init(); // 5. 解除强制断开DP上拉恢复 USB_DEV_CONFIG ~USB_DEV_FORCE_DISCONNECT; }这个做法的本质就是让DP在复位后先主动拉低再拉高给Host一个明确的状态变化。即便复位瞬间的短暂低脉冲不被Hub识别这段强制断开的时间也足够长能确保PC端重新枚举。我建议把这段“断开”保持在5ms以上有的Hub扫描周期比较长太短容易漏掉。4.4 方案四复位期间保持USB不中断如果你的应用要求MCU复位期间USB连接不能断比如设备需要持续在线那硬件方案就要重新设计。最简单的方法是外挂独立USB PHY芯片由PHY独立维护USB总线状态MCU复位时PHY继续工作由外部缓冲芯片转发数据。但这种方案成本高、逻辑复杂对大多数HID/CDC设备来说没必要。实际上对于普通USB外设“断开再枚举”才是更健康的行为。Host和设备都清楚自己的状态不会出现“假连接”。所以如果产品没有硬性在线要求建议优先考虑前三种方案。4.5 结合LAT1689的落地提示LAT1689的寄存器细节我不是很方便在这里逐条列因为不同封装、不同批次可能都有差异。实际操作时建议在数据手册里搜这几个关键词Pull-up Control / DP Pull-up EnableSoft Disconnect / DisconnectDevice Configuration Register如果芯片内部已经有DP上拉控制开关优先使用寄存器控制省掉外部MOS管。如果没有内部开关就用外部上拉加MOS管方案。另外复位后引脚复用状态一定要确认。如果GPIO默认被设为输出高哪怕没有使能USB模块DP仍然会被拉高PC端照样认为设备连接着。5. 常见问题与排查技巧实录5.1 整理成速查表现象可能原因排查方向复位后PC端仍显示设备已连接DP上拉未释放或断开时间太短抓DP/DM波形检查上拉控制电路复位后PC提示“无法识别的设备”DP重新上拉速度异常或电源时序有问题检查VBUS和VDD时序调整初始化顺序复位后必须拔插才能恢复DP在复位期间出现短暂低脉冲后迅速恢复高逻辑分析仪抓波形增加强制断开时间偶发复现十次里有两三次异常复位脚抖动或USB Hub扫描周期干扰检查NRST滤波电容延长软断开延时看门狗复位后USB失效概率高软件复位不重置USB PHY状态初始化时增加强制断开再重连换一台电脑后现象消失不同主控对断开检测的灵敏度不同以最短断开时间窗口为基准做兼容优化5.2 一个容易忽略的坑内部上拉和外部上拉并存如果你使用的MCU内部已经提供了DP上拉开关外面的板子又放了1.5kΩ电阻两个上拉并联后的等效阻值会低于1.5kΩ导致DP上的边沿速率变快还可能让复位时的电平状态变得不确定。正常情况下全速设备D对3.3V的等效电阻应该在1.5kΩ左右如果实测明显偏低就要注意是不是两路上拉同时存在。当时我在这个坑里卡了挺久一直以为外置上拉是“标配”没有意识到LAT1689内部也有上拉控制。后来把外部电阻拆掉DP波形立刻干净了很多。所以第一步先确认内部上拉是否被固件使能再决定外部电路怎么设计。5.3 建议的测试顺序遇到类似问题我最推荐的做法是“先测量、再分析、后改动”。很多人一上来就改代码加延时、改初始化顺序结果调了半天发现是上拉电阻焊错位置。我通常按照下面这个顺序来万用表测DP/DM对地和对电源的电阻判断是否存在旁路或短路。用示波器确认NRST复位时VDD和VBUS有没有跌落。用逻辑分析仪抓完整的复位前后DP/DM波形保存成截图。根据波形确认是内部上拉问题还是外部上拉问题。改代码或改电路后连续复位50次以上确认稳定。换两台不同品牌的电脑以及一个USB Hub做兼容性验证。这套流程跑下来绝大部分USB复位异常问题都能定位清楚。5.4 个人避坑心得我后来在项目里养成了一个习惯凡是带USB口的MCU板子都预留一个“DP上拉控制GPIO”。这个GPIO平时可能不用但遇到复位异常、枚举异常、驱动兼容问题的时候它就是一个非常灵活的调试口。甚至在正式方案里我也会留出MOS管的上拉控制位置默认用0Ω电阻短接方便做A/B对比。另外一个心得是USB问题一定要分清“Host侧”和“Device侧”的责任。PC端设备管理器里的状态只是最终结果不是原因。你盯着设备管理器看半天不如拿逻辑分析仪看一秒钟波形。很多“复位后PC端认为USB连接未断开”的问题根源都是设备端DP没有干净地拉低而不是系统缓存的问题。现在再遇到这类问题我基本不会慌。先抓波形再查上拉最后决定改软件还是改电路。这个思路在LAT1689上验证过也在其他几个MCU平台上复现过你下次遇到类似情况可以先从这个方向试起。
返回列表